LCOV - code coverage report
Current view: top level - Core - OOOXZManager.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 2 96 2.1 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOOXZManager.m
       4             : 
       5             : Responsible for installing and uninstalling OXZs
       6             : 
       7             : Oolite
       8             : Copyright (C) 2004-2013 Giles C Williams and contributors
       9             : 
      10             : This program is free software; you can redistribute it and/or
      11             : modify it under the terms of the GNU General Public License
      12             : as published by the Free Software Foundation; either version 2
      13             : of the License, or (at your option) any later version.
      14             : 
      15             : This program is distributed in the hope that it will be useful,
      16             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             : GNU General Public License for more details.
      19             : 
      20             : You should have received a copy of the GNU General Public License
      21             : along with this program; if not, write to the Free Software
      22             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      23             : MA 02110-1301, USA.
      24             : 
      25             : */
      26             : 
      27             : #import "OOOXZManager.h"
      28             : #import "OOPListParsing.h"
      29             : #import "OOStringParsing.h"
      30             : #import "ResourceManager.h"
      31             : #import "OOCacheManager.h"
      32             : #import "Universe.h"
      33             : #import "GuiDisplayGen.h"
      34             : #import "PlayerEntity.h"
      35             : #import "PlayerEntitySound.h"
      36             : #import "OOCollectionExtractors.h"
      37             : #import "NSFileManagerOOExtensions.h"
      38             : #import "NSDataOOExtensions.h"
      39             : #import "NSStringOOExtensions.h"
      40             : #import "OOColor.h"
      41             : #import "OOStringExpander.h"
      42             : #import "MyOpenGLView.h"
      43             : 
      44             : #import "unzip.h"
      45             : 
      46             : #import "OOManifestProperties.h"
      47             : 
      48             : /* The URL for the manifest.plist array. */
      49             : /* switching (temporarily maybe) to oolite.space - Nikos 20230507 */
      50             : /*static NSString * const kOOOXZDataURL = @"http://addons.oolite.org/api/1.0/overview";*/
      51           0 : static NSString * const kOOOXZDataURL = @"http://addons.oolite.space/api/1.0/overview";
      52             : /* The config parameter to use a non-default URL at runtime */
      53           0 : static NSString * const kOOOXZDataConfig = @"oxz-index-url";
      54             : /* The filename to store the downloaded manifest.plist array */
      55           0 : static NSString * const kOOOXZManifestCache = @"Oolite-manifests.plist";
      56             : /* The filename to temporarily store the downloaded OXZ. Has an OXZ extension since we might want to read its manifest.plist out of it;  */
      57           0 : static NSString * const kOOOXZTmpPath = @"Oolite-download.oxz";
      58             : /* The filename to temporarily store the downloaded plists. */
      59           0 : static NSString * const kOOOXZTmpPlistPath = @"Oolite-download.plist";
      60             : 
      61             : /* Log file record types */
      62           0 : static NSString * const kOOOXZErrorLog = @"oxz.manager.error";
      63           0 : static NSString * const kOOOXZDebugLog = @"oxz.manager.debug";
      64             : 
      65             : 
      66             : /* Filter components */
      67           0 : static NSString * const kOOOXZFilterAll = @"*";
      68           0 : static NSString * const kOOOXZFilterUpdates = @"u";
      69           0 : static NSString * const kOOOXZFilterInstallable = @"i";
      70           0 : static NSString * const kOOOXZFilterKeyword = @"k:";
      71           0 : static NSString * const kOOOXZFilterAuthor = @"a:";
      72           0 : static NSString * const kOOOXZFilterCategory = @"c:";
      73           0 : static NSString * const kOOOXZFilterDays = @"d:";
      74           0 : static NSString * const kOOOXZFilterTag = @"t:";
      75             : 
      76             : 
      77           0 : typedef enum {
      78             :         OXZ_INSTALLABLE_OKAY,
      79             :         OXZ_INSTALLABLE_UPDATE,
      80             :         OXZ_INSTALLABLE_DEPENDENCIES,
      81             :         OXZ_INSTALLABLE_CONFLICTS,
      82             :         // for things to work, _ALREADY must be the first UNINSTALLABLE state
      83             :         // and all the INSTALLABLE ones must be before all the UNINSTALLABLE ones
      84             :         OXZ_UNINSTALLABLE_ALREADY,
      85             :         OXZ_UNINSTALLABLE_NOREMOTE,
      86             :         OXZ_UNINSTALLABLE_VERSION,
      87             :         OXZ_UNINSTALLABLE_MANUAL
      88             : } OXZInstallableState;
      89             : 
      90             : 
      91           0 : enum {
      92             :         OXZ_GUI_ROW_LISTHEAD    = 0,
      93             :         OXZ_GUI_ROW_FIRSTRUN    = 1,
      94             :         OXZ_GUI_ROW_PROGRESS    = 1,
      95             :         OXZ_GUI_ROW_FILTERHELP  = 1,
      96             :         OXZ_GUI_ROW_LISTPREV    = 1,
      97             :         OXZ_GUI_ROW_LISTSTART   = 2,
      98             :         OXZ_GUI_NUM_LISTROWS    = 10,
      99             :         OXZ_GUI_ROW_LISTNEXT    = 12,
     100             :         OXZ_GUI_ROW_LISTSTATUS  = 14,
     101             :         OXZ_GUI_ROW_LISTDESC    = 16,
     102             :         OXZ_GUI_ROW_LISTINFO1   = 19,
     103             :         OXZ_GUI_ROW_LISTINFO2   = 20,
     104             :         OXZ_GUI_ROW_LISTFILTER  = 21,
     105             :         OXZ_GUI_ROW_INSTALL             = 22,
     106             :         OXZ_GUI_ROW_INSTALLED   = 23,
     107             :         OXZ_GUI_ROW_UPDATE_ALL  = 24,
     108             :         OXZ_GUI_ROW_REMOVE              = 25,
     109             :         OXZ_GUI_ROW_PROCEED             = 25,
     110             :         OXZ_GUI_ROW_UPDATE              = 26,
     111             :         OXZ_GUI_ROW_CANCEL              = 26,
     112             :         OXZ_GUI_ROW_FILTERCURRENT = 26,
     113             :         OXZ_GUI_ROW_INPUT               = 27,
     114             :         OXZ_GUI_ROW_EXIT                = 27
     115             : };
     116             : 
     117             : 
     118             : NSComparisonResult oxzSort(id m1, id m2, void *context);
     119             : 
     120           0 : static OOOXZManager *sSingleton = nil;
     121             : 
     122             : // protocol was only formalised in 10.7
     123             : #if OOLITE_MAC_OS_X_10_7 
     124             : @interface OOOXZManager (OOPrivate) <NSURLConnectionDataDelegate> 
     125             : #else
     126             : @interface OOOXZManager (NSURLConnectionDataDelegate) 
     127             : #endif
     128             : 
     129           0 : - (NSString *) manifestPath;
     130           0 : - (NSString *) downloadPath;
     131           0 : - (NSString *) extractionBasePathForIdentifier:(NSString *)identifier andVersion:(NSString *)version;
     132           0 : - (NSString *) dataURL;
     133           0 : - (NSString *) humanSize:(NSUInteger)bytes;
     134             : 
     135           0 : - (BOOL) ensureInstallPath;
     136             : 
     137           0 : - (BOOL) beginDownload:(NSMutableURLRequest *)request;
     138           0 : - (BOOL) processDownloadedManifests;
     139           1 : - (BOOL) processDownloadedOXZ;
     140             : 
     141           0 : - (OXZInstallableState) installableState:(NSDictionary *)manifest;
     142           0 : - (OOColor *) colorForManifest:(NSDictionary *)manifest;
     143           0 : - (NSString *) installStatusForManifest:(NSDictionary *)manifest;
     144             : 
     145           0 : - (BOOL) validateFilter:(NSString *)input;
     146             : 
     147           0 : - (void) setOXZList:(NSArray *)list;
     148           0 : - (void) setFilteredList:(NSArray *)list;
     149           0 : - (NSArray *) applyCurrentFilter:(NSArray *)list;
     150             : 
     151           0 : - (void) setCurrentDownload:(NSURLConnection *)download withLabel:(NSString *)label;
     152           0 : - (void) setProgressStatus:(NSString *)newStatus;
     153             : 
     154           0 : - (BOOL) installOXZ:(NSUInteger)item;
     155           0 : - (BOOL) updateAllOXZ;
     156           0 : - (BOOL) removeOXZ:(NSUInteger)item;
     157           0 : - (NSArray *) installOptions;
     158           0 : - (NSArray *) removeOptions;
     159             : 
     160           0 : - (NSString *) extractOXZ:(NSUInteger)item;
     161             : 
     162             : /* Delegates for URL downloader */
     163           0 : - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
     164           0 : - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
     165           0 : - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
     166           0 : - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
     167             : 
     168             : @end
     169             : 
     170             : @interface OOOXZManager (OOFilterRules)
     171           0 : - (BOOL) applyFilterByNoFilter:(NSDictionary *)manifest;
     172           0 : - (BOOL) applyFilterByUpdateRequired:(NSDictionary *)manifest;
     173           0 : - (BOOL) applyFilterByInstallable:(NSDictionary *)manifest;
     174           0 : - (BOOL) applyFilterByKeyword:(NSDictionary *)manifest keyword:(NSString *)keyword;
     175           0 : - (BOOL) applyFilterByAuthor:(NSDictionary *)manifest author:(NSString *)author;
     176           0 : - (BOOL) applyFilterByDays:(NSDictionary *)manifest days:(NSString *)days;
     177           0 : - (BOOL) applyFilterByTag:(NSDictionary *)manifest tag:(NSString *)tag;
     178           0 : - (BOOL) applyFilterByCategory:(NSDictionary *)manifest category:(NSString *)category;
     179             : 
     180             : @end 
     181             : 
     182             : 
     183             : 
     184             : @implementation OOOXZManager
     185             : 
     186             : + (OOOXZManager *)sharedManager
     187             : {
     188             :         // NOTE: assumes single-threaded first access.
     189             :         if (sSingleton == nil)  sSingleton = [[self alloc] init];
     190             :         return sSingleton;
     191             : }
     192             : 
     193             : 
     194           0 : - (id) init
     195             : {
     196             :         self = [super init];
     197             :         if (self != nil)
     198             :         {
     199             :                 _downloadStatus = OXZ_DOWNLOAD_NONE;
     200             :                 // if the file has not been downloaded, this will be nil
     201             :                 [self setOXZList:OOArrayFromFile([self manifestPath])];
     202             :                 OOLog(kOOOXZDebugLog,@"Initialised with %@",_oxzList);
     203             :                 _interfaceState = OXZ_STATE_NODATA;
     204             :                 _currentFilter = @"*";
     205             :                 
     206             :                 _interfaceShowingOXZDetail = NO;
     207             :                 _changesMade = NO;
     208             :                 _downloadAllDependencies = NO;
     209             :                 _dependencyStack = [[NSMutableSet alloc] initWithCapacity:8];
     210             :                 [self setProgressStatus:@""];
     211             :         }
     212             :         return self;
     213             : }
     214             : 
     215             : 
     216           0 : - (void)dealloc
     217             : {
     218             :         if (sSingleton == self)  sSingleton = nil;
     219             : 
     220             :         [self setCurrentDownload:nil withLabel:nil];
     221             :         DESTROY(_oxzList);
     222             :         DESTROY(_managedList);
     223             :         DESTROY(_filteredList);
     224             : 
     225             :         [super dealloc];
     226             : }
     227             : 
     228             : 
     229             : /* The install path for OXZs downloaded by
     230             :  * Oolite. Library/ApplicationSupport seems to be the most appropriate
     231             :  * location. */
     232             : - (NSString *) installPath
     233             : {
     234             :         NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask,YES);
     235             :         NSString *appPath = [paths objectAtIndex:0];
     236             :         if (appPath != nil)
     237             :         {
     238             :                 appPath = [appPath stringByAppendingPathComponent:@"Oolite"];
     239             : #if OOLITE_MAC_OS_X
     240             :                 appPath = [appPath stringByAppendingPathComponent:@"Managed AddOns"];
     241             : #else
     242             :                 /* GNUStep uses "ApplicationSupport" rather than "Application
     243             :                  * Support" so match convention by not putting a space in the
     244             :                  * path either */
     245             :                 appPath = [appPath stringByAppendingPathComponent:@"ManagedAddOns"];
     246             : #endif
     247             :                 return appPath;
     248             :         }
     249             :         return nil;
     250             : }
     251             : 
     252             : 
     253           0 : - (NSString *) extractionBasePathForIdentifier:(NSString *)identifier andVersion:(NSString *)version
     254             : {
     255             :         NSString *basePath = [[ResourceManager userRootPaths] lastObject];
     256             :         NSString *rawMainDir = [NSString stringWithFormat:@"%@-%@.off",identifier,version];
     257             :         
     258             :         NSCharacterSet *blacklist = [NSCharacterSet characterSetWithCharactersInString:@"'#%^&{}[]/~|\\?<,:\" "];
     259             :         return [[[basePath stringByAppendingPathComponent:[[rawMainDir componentsSeparatedByCharactersInSet:blacklist] componentsJoinedByString:@""]] retain] autorelease];
     260             : }
     261             : 
     262             : 
     263           0 : - (BOOL) ensureInstallPath
     264             : {
     265             :         BOOL                            exists, directory;
     266             :         NSFileManager           *fmgr = [NSFileManager defaultManager];
     267             :         NSString                        *path = [self installPath];
     268             : 
     269             :         exists = [fmgr fileExistsAtPath:path isDirectory:&directory];
     270             :         
     271             :         if (exists && !directory)
     272             :         {
     273             :                 OOLog(kOOOXZErrorLog, @"Expected %@ to be a folder, but it is a file.", path);
     274             :                 return NO;
     275             :         }
     276             :         if (!exists)
     277             :         {
     278             :                 if (![fmgr oo_createDirectoryAtPath:path attributes:nil])
     279             :                 {
     280             :                         OOLog(kOOOXZErrorLog, @"Could not create folder %@.", path);
     281             :                         return NO;
     282             :                 }
     283             :         }
     284             :         
     285             :         return YES;
     286             : }
     287             : 
     288             : 
     289           0 : - (NSString *) manifestPath
     290             : {
     291             :         return [[[OOCacheManager sharedCache] cacheDirectoryPathCreatingIfNecessary:YES] stringByAppendingPathComponent:kOOOXZManifestCache];
     292             : }
     293             : 
     294             : 
     295             : /* Download mechanism could destroy a correct file if it failed
     296             :  * half-way and was downloaded on top of the old one. So this loads it
     297             :  * off to the side a bit */
     298           0 : - (NSString *) downloadPath
     299             : {
     300             :         if (_interfaceState == OXZ_STATE_UPDATING)
     301             :         {
     302             :                 return [[[OOCacheManager sharedCache] cacheDirectoryPathCreatingIfNecessary:YES] stringByAppendingPathComponent:kOOOXZTmpPlistPath];
     303             :         }
     304             :         else
     305             :         {
     306             :                 return [[[OOCacheManager sharedCache] cacheDirectoryPathCreatingIfNecessary:YES] stringByAppendingPathComponent:kOOOXZTmpPath];
     307             :         }
     308             : }
     309             : 
     310             : 
     311           0 : - (NSString *) dataURL
     312             : {
     313             :         /* Not expected to be set in general, but might be useful for some users */
     314             :         NSString *url = [[NSUserDefaults standardUserDefaults] stringForKey:kOOOXZDataConfig];
     315             :         if (url != nil)
     316             :         {
     317             :                 return url;
     318             :         }
     319             :         return kOOOXZDataURL;
     320             : }
     321             : 
     322             : 
     323           0 : - (NSString *) humanSize:(NSUInteger)bytes
     324             : {
     325             :         if (bytes == 0)
     326             :         {
     327             :                 return DESC(@"oolite-oxzmanager-missing-field");
     328             :         }
     329             :         else if (bytes < 1024)
     330             :         {
     331             :                 return @"<1 kB";
     332             :         }
     333             :         else if (bytes < 1024*1024)
     334             :         {
     335             :                 return [NSString stringWithFormat:@"%lu kB",bytes>>10];
     336             :         }
     337             :         else 
     338             :         {
     339             :                 return [NSString stringWithFormat:@"%.2f MB",((float)(bytes>>10))/1024];
     340             :         }
     341             : }
     342             : 
     343             : 
     344           0 : - (void) setOXZList:(NSArray *)list
     345             : {
     346             :         DESTROY(_oxzList);
     347             :         if (list != nil)
     348             :         {
     349             :                 _oxzList = [[list sortedArrayUsingFunction:oxzSort context:NULL] retain];
     350             :                 // needed for update to available versions
     351             :                 DESTROY(_managedList);
     352             :         }
     353             : }
     354             : 
     355             : 
     356           0 : - (void) setFilteredList:(NSArray *)list
     357             : {
     358             :         DESTROY(_filteredList);
     359             :         _filteredList = [list copy]; // copy retains
     360             : }
     361             : 
     362             : 
     363           0 : - (void) setFilter:(NSString *)filter
     364             : {
     365             :         DESTROY(_currentFilter);
     366             :         _currentFilter = [[filter lowercaseString] copy]; // copy retains
     367             : }
     368             : 
     369             : 
     370           0 : - (NSArray *) applyCurrentFilter:(NSArray *)list
     371             : {
     372             :         SEL filterSelector = @selector(applyFilterByNoFilter:);
     373             :         NSString *parameter  = nil;
     374             :         if ([_currentFilter isEqualToString:kOOOXZFilterUpdates])
     375             :         {
     376             :                 filterSelector = @selector(applyFilterByUpdateRequired:);
     377             :         }
     378             :         else if ([_currentFilter isEqualToString:kOOOXZFilterInstallable])
     379             :         {
     380             :                 filterSelector = @selector(applyFilterByInstallable:);
     381             :         }
     382             :         else if ([_currentFilter hasPrefix:kOOOXZFilterKeyword])
     383             :         {
     384             :                 filterSelector = @selector(applyFilterByKeyword:keyword:);
     385             :                 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterKeyword length]];
     386             :         }
     387             :         else if ([_currentFilter hasPrefix:kOOOXZFilterAuthor])
     388             :         {
     389             :                 filterSelector = @selector(applyFilterByAuthor:author:);
     390             :                 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterAuthor length]];
     391             :         }
     392             :         else if ([_currentFilter hasPrefix:kOOOXZFilterDays])
     393             :         {
     394             :                 filterSelector = @selector(applyFilterByDays:days:);
     395             :                 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterDays length]];
     396             :         }
     397             :         else if ([_currentFilter hasPrefix:kOOOXZFilterTag])
     398             :         {
     399             :                 filterSelector = @selector(applyFilterByTag:tag:);
     400             :                 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterTag length]];
     401             :         }
     402             :         else if ([_currentFilter hasPrefix:kOOOXZFilterCategory])
     403             :         {
     404             :                 filterSelector = @selector(applyFilterByCategory:category:);
     405             :                 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterCategory length]];
     406             :         }
     407             : 
     408             :         NSMutableArray *filteredList = [NSMutableArray arrayWithCapacity:[list count]];
     409             :         NSDictionary *manifest       = nil;
     410             :         NSInvocation *invocation     = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:filterSelector]];
     411             :         [invocation setSelector:filterSelector];
     412             :         [invocation setTarget:self];
     413             :         if (parameter != nil)
     414             :         {
     415             :                 [invocation setArgument:&parameter atIndex:3];
     416             :         }
     417             : 
     418             :         foreach(manifest, list)
     419             :         {
     420             :                 [invocation setArgument:&manifest atIndex:2];
     421             :                 [invocation invoke];
     422             :                 BOOL filterAccepted = NO;
     423             :                 [invocation getReturnValue:&filterAccepted];
     424             :                 if (filterAccepted)
     425             :                 {
     426             :                         [filteredList addObject:manifest];
     427             :                 }
     428             :         }
     429             :         // any bad filter that gets this far is also treated as '*'
     430             :         // so don't need to explicitly test for '*' or ''
     431             :         return [[filteredList copy] autorelease];
     432             : }
     433             : 
     434             : 
     435             : /*** Start filters ***/
     436           0 : - (BOOL) applyFilterByNoFilter:(NSDictionary *)manifest
     437             : {
     438             :         return YES;
     439             : }
     440             : 
     441             : 
     442           0 : - (BOOL) applyFilterByUpdateRequired:(NSDictionary *)manifest
     443             : {
     444             :         return ([self installableState:manifest] == OXZ_INSTALLABLE_UPDATE);
     445             : }
     446             : 
     447             : 
     448           0 : - (BOOL) applyFilterByInstallable:(NSDictionary *)manifest
     449             : {
     450             :         return ([self installableState:manifest] < OXZ_UNINSTALLABLE_ALREADY);
     451             : }
     452             : 
     453             : 
     454           0 : - (BOOL) applyFilterByKeyword:(NSDictionary *)manifest keyword:(NSString *)keyword
     455             : {
     456             :         NSString *parameter = nil;
     457             :         NSArray *parameters = [NSArray arrayWithObjects:kOOManifestTitle,kOOManifestDescription,kOOManifestCategory,nil];
     458             :         
     459             :         // trim any eventual leading whitespace from input string
     460             :         keyword = [keyword stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
     461             :         
     462             :         foreach (parameter,parameters)
     463             :         {
     464             :                 if ([[manifest oo_stringForKey:parameter] rangeOfString:keyword options:NSCaseInsensitiveSearch].location != NSNotFound)
     465             :                 {
     466             :                         return YES;
     467             :                 }
     468             :         }
     469             :         // tags are slightly different
     470             :         parameters = [manifest oo_arrayForKey:kOOManifestTags];
     471             :         foreach (parameter,parameters)
     472             :         {
     473             :                 if ([parameter rangeOfString:keyword options:NSCaseInsensitiveSearch].location != NSNotFound)
     474             :                 {
     475             :                         return YES;
     476             :                 }
     477             :         }
     478             :         
     479             :         return NO;
     480             : }
     481             : 
     482             : 
     483           0 : - (BOOL) applyFilterByAuthor:(NSDictionary *)manifest author:(NSString *)author
     484             : {
     485             :         // trim any eventual leading whitespace from input string
     486             :         author = [author stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
     487             :         
     488             :         NSString *mAuth = [manifest oo_stringForKey:kOOManifestAuthor];
     489             :         return ([mAuth rangeOfString:author options:NSCaseInsensitiveSearch].location != NSNotFound);
     490             : }
     491             : 
     492             : 
     493           0 : - (BOOL) applyFilterByDays:(NSDictionary *)manifest days:(NSString *)days
     494             : {
     495             :         NSInteger i = [days integerValue];
     496             :         if (i < 1)
     497             :         {
     498             :                 return NO;
     499             :         }
     500             :         else
     501             :         {
     502             :                 NSUInteger updated = [manifest oo_unsignedIntegerForKey:kOOManifestUploadDate];
     503             :                 NSUInteger now = (NSUInteger)[[NSDate date] timeIntervalSince1970];
     504             :                 return (updated + (86400 * i) > now);
     505             :         }
     506             : }
     507             : 
     508             : 
     509           0 : - (BOOL) applyFilterByTag:(NSDictionary *)manifest tag:(NSString *)tag
     510             : {
     511             :         NSString *parameter = nil;
     512             :         NSArray *parameters = [manifest oo_arrayForKey:kOOManifestTags];
     513             : 
     514             :         // trim any eventual leading whitespace from input string
     515             :         tag = [tag stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
     516             :         
     517             :         foreach (parameter,parameters)
     518             :         {
     519             :                 if ([parameter rangeOfString:tag options:NSCaseInsensitiveSearch].location != NSNotFound)
     520             :                 {
     521             :                         return YES;
     522             :                 }
     523             :         }
     524             :         
     525             :         return NO;
     526             : }
     527             : 
     528             : 
     529           0 : - (BOOL) applyFilterByCategory:(NSDictionary *)manifest category:(NSString *)category
     530             : {
     531             :         // trim any eventual leading whitespace from input string
     532             :         category = [category stringByTrimmingLeadingWhitespaceAndNewlineCharacters];
     533             :         
     534             :         NSString *mCategory = [manifest oo_stringForKey:kOOManifestCategory];
     535             :         return ([mCategory rangeOfString:category options:NSCaseInsensitiveSearch].location != NSNotFound);
     536             : }
     537             : 
     538             : 
     539             : /*** End filters ***/
     540             : 
     541           0 : - (BOOL) validateFilter:(NSString *)input
     542             : {
     543             :         NSString *filter = [input lowercaseString];
     544             :         if (([filter length] == 0) // empty is valid
     545             :                 || ([filter isEqualToString:kOOOXZFilterAll])
     546             :                 || ([filter isEqualToString:kOOOXZFilterUpdates])
     547             :                 || ([filter isEqualToString:kOOOXZFilterInstallable])
     548             :                 || ([filter hasPrefix:kOOOXZFilterKeyword] && [filter length] > [kOOOXZFilterKeyword length])
     549             :                 || ([filter hasPrefix:kOOOXZFilterAuthor] && [filter length] > [kOOOXZFilterAuthor length])
     550             :                 || ([filter hasPrefix:kOOOXZFilterDays] && [[filter substringFromIndex:[kOOOXZFilterDays length]] intValue] > 0)
     551             :                 || ([filter hasPrefix:kOOOXZFilterTag] && [filter length] > [kOOOXZFilterTag length])
     552             :                 || ([filter hasPrefix:kOOOXZFilterCategory] && [filter length] > [kOOOXZFilterCategory length])
     553             :                 )
     554             :         {
     555             :                 return YES;
     556             :         }
     557             : 
     558             :         return NO;
     559             : }
     560             : 
     561             : 
     562           0 : - (void) setCurrentDownload:(NSURLConnection *)download withLabel:(NSString *)label
     563             : {
     564             :         if (_currentDownload != nil)
     565             :         {
     566             :                 [_currentDownload cancel]; // releases via delegate
     567             :         }
     568             :         _currentDownload = [download retain];
     569             :         DESTROY(_currentDownloadName);
     570             :         _currentDownloadName = [label copy];
     571             : }
     572             : 
     573             : 
     574           0 : - (void) setProgressStatus:(NSString *)new
     575             : {
     576             :         DESTROY(_progressStatus);
     577             :         _progressStatus = [new copy];
     578             : }
     579             : 
     580             : - (BOOL) updateManifests
     581             : {
     582             :         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self dataURL]]];
     583             :         if (_downloadStatus != OXZ_DOWNLOAD_NONE)
     584             :         {
     585             :                 return NO;
     586             :         }
     587             :         _downloadStatus = OXZ_DOWNLOAD_STARTED;
     588             :         _interfaceState = OXZ_STATE_UPDATING;
     589             :         [self setProgressStatus:@""];
     590             : 
     591             :         return [self beginDownload:request];
     592             : }
     593             : 
     594             : 
     595           0 : - (BOOL) beginDownload:(NSMutableURLRequest *)request
     596             : {
     597             :         NSString *userAgent = [NSString stringWithFormat:@"Oolite/%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]];
     598             :         [request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
     599             :         NSURLConnection *download = [[NSURLConnection alloc] initWithRequest:request delegate:self];
     600             :         if (download)
     601             :         {
     602             :                 _downloadProgress = 0;
     603             :                 _downloadExpected = 0;
     604             :                 NSString *label = DESC(@"oolite-oxzmanager-download-label-list");
     605             :                 if (_interfaceState != OXZ_STATE_UPDATING)
     606             :                 {
     607             :                         NSDictionary *expectedManifest = nil;
     608             :                         expectedManifest = [_filteredList objectAtIndex:_item];
     609             : 
     610             :                         label = [expectedManifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-download-label-oxz")];
     611             :                 }
     612             : 
     613             :                 [self setCurrentDownload:download withLabel:label]; // retains it
     614             :                 [download release];
     615             :                 OOLog(kOOOXZDebugLog,@"Download request received, using %@ and downloading to %@",[request URL],[self downloadPath]);
     616             :                 return YES;
     617             :         }
     618             :         else
     619             :         {
     620             :                 OOLog(kOOOXZErrorLog,@"Unable to start downloading file at %@",[request URL]);
     621             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     622             :                 return NO;
     623             :         }
     624             : }
     625             : 
     626             : 
     627             : - (BOOL) cancelUpdate
     628             : {
     629             :         if (!(_interfaceState == OXZ_STATE_UPDATING || _interfaceState == OXZ_STATE_INSTALLING) || _downloadStatus == OXZ_DOWNLOAD_NONE)
     630             :         {
     631             :                 return NO;
     632             :         }
     633             :         OOLog(kOOOXZDebugLog, @"%@", @"Trying to cancel file download");
     634             :         if (_currentDownload != nil)
     635             :         {
     636             :                 [_currentDownload cancel];
     637             :         }
     638             :         else if (_downloadStatus == OXZ_DOWNLOAD_COMPLETE)
     639             :         {
     640             :                 NSString *path = [self downloadPath];
     641             :                 [[NSFileManager defaultManager] oo_removeItemAtPath:path];
     642             :         }
     643             :         _downloadStatus = OXZ_DOWNLOAD_NONE;
     644             :         if (_interfaceState == OXZ_STATE_INSTALLING)
     645             :         {
     646             :                 _interfaceState = OXZ_STATE_PICK_INSTALL;
     647             :         }
     648             :         else
     649             :         {
     650             :                 _interfaceState = OXZ_STATE_MAIN;
     651             :         }
     652             :         [self gui];
     653             :         return YES;
     654             : }
     655             : 
     656             : 
     657             : - (NSArray *) manifests
     658             : {
     659             :         return _oxzList;
     660             : }
     661             : 
     662             : 
     663             : - (NSArray *) managedOXZs
     664             : {
     665             :         if (_managedList == nil)
     666             :         {
     667             :                 // if this list is being reset, also reset the current install list
     668             :                 [ResourceManager resetManifestKnowledgeForOXZManager];
     669             :                 NSArray *managedOXZs = [[NSFileManager defaultManager] oo_directoryContentsAtPath:[self installPath]];
     670             :                 NSMutableArray *manifests = [NSMutableArray arrayWithCapacity:[managedOXZs count]];
     671             :                 NSString *filename = nil;
     672             :                 NSString *fullpath = nil;
     673             :                 NSDictionary *manifest = nil;
     674             :                 foreach (filename, managedOXZs)
     675             :                 {
     676             :                         fullpath = [[self installPath] stringByAppendingPathComponent:filename];
     677             :                         manifest = OODictionaryFromFile([fullpath stringByAppendingPathComponent:@"manifest.plist"]);
     678             :                         if (manifest != nil)
     679             :                         {
     680             :                                 NSMutableDictionary *adjManifest = [NSMutableDictionary dictionaryWithDictionary:manifest];
     681             :                                 [adjManifest setObject:fullpath forKey:kOOManifestFilePath];
     682             : 
     683             :                                 NSDictionary *stored = nil;
     684             :                                 /* The list is already sorted to put the latest
     685             :                                  * versions first. This flag means that it stops
     686             :                                  * checking the list for versions once it finds one
     687             :                                  * that is plausibly installable */
     688             :                                 BOOL foundInstallable = NO;
     689             :                                 foreach (stored, _oxzList)
     690             :                                 {
     691             :                                         if ([[stored oo_stringForKey:kOOManifestIdentifier] isEqualToString:[manifest oo_stringForKey:kOOManifestIdentifier]])
     692             :                                         {
     693             :                                                 if (foundInstallable == NO)
     694             :                                                 {
     695             :                                                         [adjManifest setObject:[stored oo_stringForKey:kOOManifestVersion] forKey:kOOManifestAvailableVersion];
     696             :                                                         [adjManifest setObject:[stored oo_stringForKey:kOOManifestDownloadURL] forKey:kOOManifestDownloadURL];
     697             :                                                         if ([ResourceManager checkVersionCompatibility:manifest forOXP:nil])
     698             :                                                         {
     699             :                                                                 foundInstallable = YES;
     700             :                                                         }
     701             :                                                 }
     702             :                                         }
     703             :                                 }
     704             : 
     705             :                                 [manifests addObject:adjManifest];
     706             :                         }
     707             :                 }
     708             :                 [manifests sortUsingFunction:oxzSort context:NULL];
     709             : 
     710             :                 _managedList = [manifests copy];
     711             :         }
     712             :         return _managedList;
     713             : }
     714             : 
     715             : 
     716           0 : - (BOOL) processDownloadedManifests
     717             : {
     718             :         if (_downloadStatus != OXZ_DOWNLOAD_COMPLETE)
     719             :         {
     720             :                 return NO;
     721             :         }
     722             :         [self setOXZList:OOArrayFromFile([self downloadPath])];
     723             :         if (_oxzList != nil)
     724             :         {
     725             :                 [_oxzList writeToFile:[self manifestPath] atomically:YES];
     726             :                 // and clean up the temp file
     727             :                 [[NSFileManager defaultManager] oo_removeItemAtPath:[self downloadPath]];
     728             :                 // invalidate the managed list
     729             :                 DESTROY(_managedList);
     730             :                 _interfaceState = OXZ_STATE_TASKDONE;
     731             :                 [self gui];
     732             :                 return YES;
     733             :         }
     734             :         else
     735             :         {
     736             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     737             :                 OOLog(kOOOXZErrorLog,@"Downloaded manifest was not a valid plist, has been left in %@",[self downloadPath]);
     738             :                 // revert to the old one
     739             :                 [self setOXZList:OOArrayFromFile([self manifestPath])];
     740             :                 _interfaceState = OXZ_STATE_TASKDONE;
     741             :                 [self gui];
     742             :                 return NO;
     743             :         }
     744             : }
     745             : 
     746             : 
     747           1 : - (BOOL) processDownloadedOXZ
     748             : {
     749             :         if (_downloadStatus != OXZ_DOWNLOAD_COMPLETE)
     750             :         {
     751             :                 return NO;
     752             :         }
     753             : 
     754             :         NSDictionary *downloadedManifest = OODictionaryFromFile([[self downloadPath] stringByAppendingPathComponent:@"manifest.plist"]);
     755             :         if (downloadedManifest == nil)
     756             :         {
     757             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     758             :                 OOLog(kOOOXZErrorLog,@"Downloaded OXZ does not contain a manifest.plist, has been left in %@",[self downloadPath]);
     759             :                 _interfaceState = OXZ_STATE_TASKDONE;
     760             :                 [self gui];
     761             :                 return NO;
     762             :         }
     763             :         NSDictionary *expectedManifest = nil;
     764             :         expectedManifest = [_filteredList objectAtIndex:_item];
     765             : 
     766             :         if (expectedManifest == nil || 
     767             :                 (![[downloadedManifest oo_stringForKey:kOOManifestIdentifier] isEqualToString:[expectedManifest oo_stringForKey:kOOManifestIdentifier]]) || 
     768             :                 (![[downloadedManifest oo_stringForKey:kOOManifestVersion] isEqualToString:[expectedManifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[expectedManifest oo_stringForKey:kOOManifestVersion]]])
     769             :                 )
     770             :         {
     771             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     772             :                 OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ does not have the same identifer and version as expected. This might be due to your manifests list being out of date - try updating it.");
     773             :                 _interfaceState = OXZ_STATE_TASKDONE;
     774             :                 [self gui];
     775             :                 return NO;
     776             :         }
     777             :         // this appears to be the OXZ we expected
     778             :         // filename is going to be identifier.oxz
     779             :         NSString *filename = [[downloadedManifest oo_stringForKey:kOOManifestIdentifier] stringByAppendingString:@".oxz"];
     780             : 
     781             :         if (![self ensureInstallPath])
     782             :         {
     783             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     784             :                 OOLog(kOOOXZErrorLog, @"%@", @"Unable to create installation folder.");
     785             :                 _interfaceState = OXZ_STATE_TASKDONE;
     786             :                 [self gui];
     787             :                 return NO;
     788             :         }
     789             : 
     790             :         // delete filename if it exists from OXZ folder
     791             :         NSString *destination = [[self installPath] stringByAppendingPathComponent:filename];
     792             :         [[NSFileManager defaultManager] oo_removeItemAtPath:destination];
     793             : 
     794             :         // move the temp file on to it
     795             :         if (![[NSFileManager defaultManager] oo_moveItemAtPath:[self downloadPath] toPath:destination])
     796             :         {
     797             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     798             :                 OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
     799             :                 _interfaceState = OXZ_STATE_TASKDONE;
     800             :                 [self gui];
     801             :                 return NO;
     802             :         }
     803             :         _changesMade = YES;
     804             :         DESTROY(_managedList); // will need updating
     805             :         // do this now to cope with circular dependencies on download
     806             :         [ResourceManager resetManifestKnowledgeForOXZManager];
     807             : 
     808             :         /** 
     809             :          * If downloadedManifest is in _dependencyStack, remove it
     810             :          * Get downloadedManifest requires_oxp list
     811             :          * Add entries ones to _dependencyStack
     812             :          * If _dependencyStack has contents, update _progressStatus
     813             :          * ...and start the download of the 'first' item in _dependencyStack
     814             :          * ...which isn't already installed (_dependencyStack is unordered
     815             :          * ...so 'first' isn't really defined)
     816             :          *
     817             :          * ...if the item in _dependencyStack is not findable (e.g. wrong
     818             :          * ...version) then stop here.
     819             :          */
     820             :         NSArray *requires = [downloadedManifest oo_arrayForKey:kOOManifestRequiresOXPs defaultValue:nil];
     821             :         if (requires == nil)
     822             :         {
     823             :                 // just in case the requirements are only specified in the online copy
     824             :                 requires = [expectedManifest oo_arrayForKey:kOOManifestRequiresOXPs defaultValue:nil];
     825             :         }
     826             :         NSDictionary *requirement = nil;
     827             :         NSMutableString *progress = [NSMutableString stringWithCapacity:2048];
     828             :         OOLog(kOOOXZDebugLog,@"Dependency stack has %lu elements",[_dependencyStack count]);
     829             : 
     830             :         if ([_dependencyStack count] > 0)
     831             :         {
     832             :                 // will remove as iterate, so create a temp copy to iterate over
     833             :                 NSSet *tempStack = [NSSet setWithSet:_dependencyStack];
     834             :                 foreach (requirement, tempStack)
     835             :                 {
     836             :                         OOLog(kOOOXZDebugLog,@"Dependency stack: checking %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     837             :                         if (![ResourceManager manifest:downloadedManifest HasUnmetDependency:requirement logErrors:NO]
     838             :                             && requires != nil && [requires containsObject:requirement])
     839             :                         {
     840             :                                 // it was unmet, but now it's met                                       
     841             :                                 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-now-has-@"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     842             :                                 [_dependencyStack removeObject:requirement];
     843             :                                 OOLog(kOOOXZDebugLog, @"%@", @"Dependency stack: requirement met");
     844             :                         } else if ([[requirement oo_stringForKey:kOOManifestRelationIdentifier] isEqualToString:[downloadedManifest oo_stringForKey:kOOManifestIdentifier]]) {
     845             :                                 // remove the requirement for the just downloaded OXP
     846             :                                 [_dependencyStack removeObject:requirement];
     847             :                         }
     848             :                 }
     849             :         }
     850             :         if (requires != nil)
     851             :         {
     852             :                 foreach (requirement, requires)
     853             :                 {
     854             :                         if ([ResourceManager manifest:downloadedManifest HasUnmetDependency:requirement logErrors:NO])
     855             :                         {
     856             :                                 OOLog(kOOOXZDebugLog,@"Dependency stack: adding %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     857             :                                 [_dependencyStack addObject:requirement];
     858             :                                 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-requires-@"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     859             :                         }
     860             :                 }
     861             :         }
     862             :         if ([_dependencyStack count] > 0)
     863             :         {
     864             :                 // get an object from the requirements list, and download it
     865             :                 // if it can be found
     866             :                 BOOL undownloadedRequirement = NO;
     867             :                 NSDictionary *availableDownload = nil;
     868             :                 BOOL foundDownload = NO;
     869             :                 NSUInteger index = 0;
     870             :                 NSString *needsIdentifier = nil;
     871             : 
     872             :                 do
     873             :                 {
     874             :                         undownloadedRequirement = YES;
     875             :                         requirement = [_dependencyStack anyObject];
     876             :                         OOLog(kOOOXZDebugLog,@"Dependency stack: next is %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     877             : 
     878             :                         if (!_downloadAllDependencies)
     879             :                         {
     880             :                                 [progress appendString:DESC(@"oolite-oxzmanager-progress-get-required")];
     881             :                         }
     882             :                         needsIdentifier = [requirement oo_stringForKey:kOOManifestRelationIdentifier];
     883             :                 
     884             :                         foreach (availableDownload, _oxzList)
     885             :                         {
     886             :                                 if ([[availableDownload oo_stringForKey:kOOManifestIdentifier] isEqualToString:needsIdentifier])
     887             :                                 {
     888             :                                         if ([ResourceManager matchVersions:requirement withVersion:[availableDownload oo_stringForKey:kOOManifestVersion]])
     889             :                                         {
     890             :                                                 OOLog(kOOOXZDebugLog, @"%@", @"Dependency stack: found download for next item");
     891             :                                                 foundDownload = YES;
     892             :                                                 index = [_oxzList indexOfObject:availableDownload];
     893             :                                                 break;
     894             :                                         }
     895             :                                 }
     896             :                         }
     897             :                         
     898             :                         if (foundDownload)
     899             :                         {
     900             :                                 if ([self installableState:[_oxzList objectAtIndex:index]] == OXZ_UNINSTALLABLE_ALREADY)
     901             :                                 {
     902             :                                         OOLog(kOOOXZDebugLog,@"Dependency stack: %@ is downloaded but not yet loadable, removing from list.",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     903             :                                         // then this has already been downloaded, but
     904             :                                         // can't be configured yet presumably because
     905             :                                         // another dependency is still to be loaded
     906             :                                         [_dependencyStack removeObject:requirement];
     907             :                                         if ([_dependencyStack count] > 0)
     908             :                                         {
     909             :                                                 // try again
     910             :                                                 undownloadedRequirement = NO;
     911             :                                         }
     912             :                                         else
     913             :                                         {
     914             :                                                 // this case should probably never happen
     915             :                                                 // is handled below just in case
     916             :                                                 foundDownload = NO;
     917             :                                         }
     918             :                                 }
     919             :                         }
     920             :                 }
     921             :                 while (!undownloadedRequirement);
     922             : 
     923             :                 if (foundDownload)
     924             :                 {
     925             :                         // must clear filters entirely at this point
     926             :                         [self setFilteredList:_oxzList];
     927             :                         // then download that item
     928             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
     929             :                         if (_downloadAllDependencies)
     930             :                         {
     931             :                                 OOLog(kOOOXZDebugLog,@"Dependency stack: installing %lu from list",index);
     932             :                                 if (![self installOXZ:index]) {
     933             :                                         // if a required dependency is somehow uninstallable
     934             :                                         // e.g. required+maximum version don't match this Oolite
     935             :                                         [progress appendFormat:DESC(@"oolite-oxzmanager-progress-required-@-not-found"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     936             :                                         [self setProgressStatus:progress];
     937             :                                         OOLog(kOOOXZErrorLog,@"OXZ dependency %@ could not be found for automatic download.",needsIdentifier);
     938             :                                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
     939             :                                         OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
     940             :                                         _interfaceState = OXZ_STATE_TASKDONE;
     941             :                                         [self gui];
     942             :                                         return NO;
     943             :                                 }
     944             :                         }
     945             :                         else
     946             :                         {
     947             :                                 _interfaceState = OXZ_STATE_DEPENDENCIES;
     948             :                                 _item = index;
     949             :                         }
     950             :                         [self setProgressStatus:progress];
     951             :                         [self gui];
     952             :                         return YES;
     953             :                 }
     954             :                 // this is probably always the case, see above
     955             :                 else if ([_dependencyStack count] > 0)
     956             :                 {
     957             :                         [progress appendFormat:DESC(@"oolite-oxzmanager-progress-required-@-not-found"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     958             :                         [self setProgressStatus:progress];
     959             :                         OOLog(kOOOXZErrorLog,@"OXZ dependency %@ could not be found for automatic download.",needsIdentifier);
     960             :                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
     961             :                         OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
     962             :                         _interfaceState = OXZ_STATE_TASKDONE;
     963             :                         [self gui];
     964             :                         return NO;
     965             :                 }
     966             :         }
     967             : 
     968             :         [self setProgressStatus:@""];
     969             :         _interfaceState = OXZ_STATE_TASKDONE;
     970             :         [_dependencyStack removeAllObjects]; // just in case
     971             :         _downloadAllDependencies = NO;
     972             :         [self gui];
     973             :         return YES;
     974             : }
     975             : 
     976             : 
     977           0 : - (NSDictionary *) installedManifestForIdentifier:(NSString *)identifier
     978             : {
     979             :         NSArray *installed = [self managedOXZs];
     980             :         NSDictionary *manifest = nil;
     981             :         foreach (manifest,installed)
     982             :         {
     983             :                 if ([[manifest oo_stringForKey:kOOManifestIdentifier] isEqualToString:identifier])
     984             :                 {
     985             :                         return manifest;
     986             :                 }
     987             :         }
     988             :         return nil;
     989             : }
     990             : 
     991             : 
     992           0 : - (OXZInstallableState) installableState:(NSDictionary *)manifest
     993             : {
     994             :         NSString *title = [manifest oo_stringForKey:kOOManifestTitle defaultValue:nil];
     995             :         NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier defaultValue:nil];
     996             :         /* Check Oolite version */
     997             :         if (![ResourceManager checkVersionCompatibility:manifest forOXP:title])
     998             :         {
     999             :                 return OXZ_UNINSTALLABLE_VERSION;
    1000             :         }
    1001             :         /* Check for current automated install */
    1002             :         NSDictionary *installed = [self installedManifestForIdentifier:identifier];
    1003             :         if (installed == nil)
    1004             :         {
    1005             :                 // check for manual install
    1006             :                 installed = [ResourceManager manifestForIdentifier:identifier];
    1007             :         }
    1008             : 
    1009             :         if (installed != nil)
    1010             :         {
    1011             :                 if (![[installed oo_stringForKey:kOOManifestFilePath] hasPrefix:[self installPath]])
    1012             :                 {
    1013             :                         // installed manually
    1014             :                         return OXZ_UNINSTALLABLE_MANUAL;
    1015             :                 }
    1016             :                 if ([[installed oo_stringForKey:kOOManifestVersion] isEqualToString:[manifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[manifest oo_stringForKey:kOOManifestVersion]]]
    1017             :                         && [[NSFileManager defaultManager] fileExistsAtPath:[installed oo_stringForKey:kOOManifestFilePath]])
    1018             :                 {
    1019             :                         // installed this exact version already, and haven't
    1020             :                         // uninstalled it since entering the manager, and it's
    1021             :                         // still available
    1022             :                         return OXZ_UNINSTALLABLE_ALREADY;
    1023             :                 }
    1024             :                 else if ([installed oo_stringForKey:kOOManifestAvailableVersion defaultValue:nil] == nil)
    1025             :                 {
    1026             :                         // installed, but no remote copy is indexed any more
    1027             :                         return OXZ_UNINSTALLABLE_NOREMOTE;
    1028             :                 }
    1029             :         }
    1030             :         /* Check for dependencies being met */
    1031             :         if ([ResourceManager manifestHasConflicts:manifest logErrors:NO])
    1032             :         {
    1033             :                 return OXZ_INSTALLABLE_CONFLICTS;
    1034             :         }
    1035             :         if (installed != nil)
    1036             :         {
    1037             :                 NSString *availableVersion = [manifest oo_stringForKey:kOOManifestAvailableVersion];
    1038             :                 if (availableVersion == nil)
    1039             :                 {
    1040             :                         availableVersion = [manifest oo_stringForKey:kOOManifestVersion];
    1041             :                 }
    1042             :                 NSString *installedVersion = [installed oo_stringForKey:kOOManifestVersion];
    1043             :                 OOLog(@"version.debug",@"%@ mv:%@ mav:%@",identifier,installedVersion,availableVersion);
    1044             :                 if (CompareVersions(ComponentsFromVersionString(installedVersion),ComponentsFromVersionString(availableVersion)) == NSOrderedDescending)
    1045             :                 {
    1046             :                         // the installed copy is more recent than the server copy
    1047             :                         return OXZ_UNINSTALLABLE_NOREMOTE;
    1048             :                 }
    1049             :                 return OXZ_INSTALLABLE_UPDATE;
    1050             :         }
    1051             :         if ([ResourceManager manifestHasMissingDependencies:manifest logErrors:NO])
    1052             :         {
    1053             :                 return OXZ_INSTALLABLE_DEPENDENCIES;
    1054             :         }
    1055             :         return OXZ_INSTALLABLE_OKAY;
    1056             : }
    1057             : 
    1058             : 
    1059           0 : - (OOColor *) colorForManifest:(NSDictionary *)manifest 
    1060             : {
    1061             :         switch ([self installableState:manifest])
    1062             :         {
    1063             :         case OXZ_INSTALLABLE_OKAY:
    1064             :                 return [OOColor yellowColor];
    1065             :         case OXZ_INSTALLABLE_UPDATE:
    1066             :                 return [OOColor cyanColor];
    1067             :         case OXZ_INSTALLABLE_DEPENDENCIES:
    1068             :                 return [OOColor orangeColor];
    1069             :         case OXZ_INSTALLABLE_CONFLICTS:
    1070             :                 return [OOColor brownColor];
    1071             :         case OXZ_UNINSTALLABLE_ALREADY:
    1072             :                 return [OOColor whiteColor];
    1073             :         case OXZ_UNINSTALLABLE_MANUAL:
    1074             :                 return [OOColor redColor];
    1075             :         case OXZ_UNINSTALLABLE_VERSION:
    1076             :                 return [OOColor grayColor];
    1077             :         case OXZ_UNINSTALLABLE_NOREMOTE:
    1078             :                 return [OOColor blueColor];
    1079             :         }
    1080             :         return [OOColor yellowColor]; // never
    1081             : }
    1082             : 
    1083             : 
    1084           0 : - (NSString *) installStatusForManifest:(NSDictionary *)manifest 
    1085             : {
    1086             :         switch ([self installableState:manifest])
    1087             :         {
    1088             :         case OXZ_INSTALLABLE_OKAY:
    1089             :                 return DESC(@"oolite-oxzmanager-installable-okay");
    1090             :         case OXZ_INSTALLABLE_UPDATE:
    1091             :                 return DESC(@"oolite-oxzmanager-installable-update");
    1092             :         case OXZ_INSTALLABLE_DEPENDENCIES:
    1093             :                 return DESC(@"oolite-oxzmanager-installable-depend");
    1094             :         case OXZ_INSTALLABLE_CONFLICTS:
    1095             :                 return DESC(@"oolite-oxzmanager-installable-conflicts");
    1096             :         case OXZ_UNINSTALLABLE_ALREADY:
    1097             :                 return DESC(@"oolite-oxzmanager-installable-already");
    1098             :         case OXZ_UNINSTALLABLE_MANUAL:
    1099             :                 return DESC(@"oolite-oxzmanager-installable-manual");
    1100             :         case OXZ_UNINSTALLABLE_VERSION:
    1101             :                 return DESC(@"oolite-oxzmanager-installable-version");
    1102             :         case OXZ_UNINSTALLABLE_NOREMOTE:
    1103             :                 return DESC(@"oolite-oxzmanager-installable-noremote");
    1104             :         }
    1105             :         return nil; // never
    1106             : }
    1107             : 
    1108             : 
    1109             : 
    1110             : - (void) gui
    1111             : {
    1112             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1113             :         OOGUIRow                startRow = OXZ_GUI_ROW_EXIT;
    1114             : 
    1115             : #if OOLITE_WINDOWS
    1116             :         /* unlock OXZs ahead of potential changes by making sure sound
    1117             :          * files aren't being held open */
    1118             :         [ResourceManager clearCaches];
    1119             :         [PLAYER destroySound];
    1120             : #endif
    1121             : 
    1122             :         [gui clearAndKeepBackground:YES];
    1123             :         [gui setTitle:DESC(@"oolite-oxzmanager-title")];
    1124             : 
    1125             :         /* This switch will give warnings unless all states are
    1126             :          * covered. */
    1127             :         switch (_interfaceState)
    1128             :         {
    1129             :         case OXZ_STATE_SETFILTER:
    1130             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-setfilter")];
    1131             :                 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-currentfilter-is-@"),_currentFilter] forRow:OXZ_GUI_ROW_FILTERCURRENT align:GUI_ALIGN_LEFT];
    1132             :                 [gui addLongText:DESC(@"oolite-oxzmanager-filterhelp") startingAtRow:OXZ_GUI_ROW_FILTERHELP align:GUI_ALIGN_LEFT];
    1133             : 
    1134             :                 
    1135             :                 return; // don't do normal row selection stuff
    1136             :         case OXZ_STATE_NODATA:
    1137             :                 if (_oxzList == nil)
    1138             :                 {
    1139             :                         [gui addLongText:DESC(@"oolite-oxzmanager-firstrun") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1140             :                         [gui setText:DESC(@"oolite-oxzmanager-download-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1141             :                         [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
    1142             : 
    1143             :                         startRow = OXZ_GUI_ROW_UPDATE;
    1144             :                 }
    1145             :                 else
    1146             :                 {
    1147             :                         // update data  
    1148             :                         [gui addLongText:DESC(@"oolite-oxzmanager-secondrun") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1149             :                         [gui setText:DESC(@"oolite-oxzmanager-download-noupdate") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1150             :                         [gui setKey:@"_MAIN" forRow:OXZ_GUI_ROW_PROCEED];
    1151             : 
    1152             :                         [gui setText:DESC(@"oolite-oxzmanager-update-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1153             :                         [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
    1154             : 
    1155             :                         startRow = OXZ_GUI_ROW_PROCEED;
    1156             :                 }
    1157             :                 break;
    1158             :         case OXZ_STATE_RESTARTING:
    1159             :                 [gui addLongText:DESC(@"oolite-oxzmanager-restart") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1160             :                 return; // yes, return, not break: controls are pointless here
    1161             :         case OXZ_STATE_MAIN:
    1162             :                 [gui addLongText:DESC(@"oolite-oxzmanager-intro") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1163             :                 // fall through
    1164             :         case OXZ_STATE_PICK_INSTALL:
    1165             :         case OXZ_STATE_PICK_INSTALLED:
    1166             :         case OXZ_STATE_PICK_REMOVE:
    1167             :                 if (_interfaceState != OXZ_STATE_MAIN)
    1168             :                 {
    1169             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-currentfilter-is-@-@"),OOExpand(@"[oolite_key_oxzmanager_setfilter]"),_currentFilter] forRow:OXZ_GUI_ROW_LISTFILTER align:GUI_ALIGN_LEFT];
    1170             :                         [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTFILTER];
    1171             :                 }
    1172             : 
    1173             :                 [gui setText:DESC(@"oolite-oxzmanager-install") forRow:OXZ_GUI_ROW_INSTALL align:GUI_ALIGN_CENTER];
    1174             :                 [gui setKey:@"_INSTALL" forRow:OXZ_GUI_ROW_INSTALL];
    1175             :                 [gui setText:DESC(@"oolite-oxzmanager-installed") forRow:OXZ_GUI_ROW_INSTALLED align:GUI_ALIGN_CENTER];
    1176             :                 [gui setKey:@"_INSTALLED" forRow:OXZ_GUI_ROW_INSTALLED];
    1177             :                 [gui setText:DESC(@"oolite-oxzmanager-remove") forRow:OXZ_GUI_ROW_REMOVE align:GUI_ALIGN_CENTER];
    1178             :                 [gui setKey:@"_REMOVE" forRow:OXZ_GUI_ROW_REMOVE];
    1179             :                 [gui setText:DESC(@"oolite-oxzmanager-update-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1180             :                 [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
    1181             :                 [gui setText:DESC(@"oolite-oxzmanager-update-all") forRow:OXZ_GUI_ROW_UPDATE_ALL align:GUI_ALIGN_CENTER];
    1182             :                 [gui setKey:@"_UPDATE_ALL" forRow:OXZ_GUI_ROW_UPDATE_ALL];
    1183             : 
    1184             :                 startRow = OXZ_GUI_ROW_INSTALL;
    1185             :                 break;
    1186             :         case OXZ_STATE_UPDATING:
    1187             :         case OXZ_STATE_INSTALLING:
    1188             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-downloading")];
    1189             : 
    1190             :                 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-progress-@-is-@-of-@"),_currentDownloadName,[self humanSize:_downloadProgress],[self humanSize:_downloadExpected]] startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1191             : 
    1192             :                 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+2 align:GUI_ALIGN_LEFT];
    1193             : 
    1194             :                 [gui setText:DESC(@"oolite-oxzmanager-cancel") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
    1195             :                 [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
    1196             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1197             :                 break;
    1198             :         case OXZ_STATE_DEPENDENCIES:
    1199             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-dependencies")];
    1200             : 
    1201             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-decision") forRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1202             : 
    1203             :                 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+2 align:GUI_ALIGN_LEFT];
    1204             : 
    1205             :                 startRow = OXZ_GUI_ROW_INSTALLED;
    1206             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-yes-all") forRow:OXZ_GUI_ROW_INSTALLED align:GUI_ALIGN_CENTER];
    1207             :                 [gui setKey:@"_PROCEED_ALL" forRow:OXZ_GUI_ROW_INSTALLED];
    1208             : 
    1209             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-yes") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1210             :                 [gui setKey:@"_PROCEED" forRow:OXZ_GUI_ROW_PROCEED];
    1211             : 
    1212             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-no") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
    1213             :                 [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
    1214             :                 break;
    1215             : 
    1216             :         case OXZ_STATE_REMOVING:
    1217             :                 [gui addLongText:DESC(@"oolite-oxzmanager-removal-done") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1218             :                 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1219             :                 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
    1220             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1221             :                 break;
    1222             :         case OXZ_STATE_TASKDONE:
    1223             :                 if (_downloadStatus == OXZ_DOWNLOAD_COMPLETE)
    1224             :                 {
    1225             :                         [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-progress-done-%u-%u"),[_oxzList count],[[self managedOXZs] count]] startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1226             :                 }
    1227             :                 else
    1228             :                 {
    1229             :                         [gui addLongText:OOExpandKey(@"oolite-oxzmanager-progress-error") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1230             :                 }
    1231             :                 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+4 align:GUI_ALIGN_LEFT];
    1232             : 
    1233             :                 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1234             :                 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
    1235             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1236             :                 break;
    1237             :         case OXZ_STATE_EXTRACT:
    1238             :                 {
    1239             :                         NSDictionary *manifest = [_filteredList oo_dictionaryAtIndex:_item];
    1240             :                         NSString *title = [manifest oo_stringForKey:kOOManifestTitle];
    1241             :                         NSString *version = [manifest oo_stringForKey:kOOManifestVersion];
    1242             :                         NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier];
    1243             :                         [gui setTitle:DESC(@"oolite-oxzmanager-title-extract")];
    1244             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-title-@-version-@"),
    1245             :                                                                    title,
    1246             :                                                                    version]
    1247             :                                   forRow:0 align:GUI_ALIGN_LEFT];
    1248             :                         [gui addLongText:DESC(@"oolite-oxzmanager-extract-info") startingAtRow:2 align:GUI_ALIGN_LEFT];
    1249             : #ifdef NDEBUG
    1250             :                         [gui addLongText:DESC(@"oolite-oxzmanager-extract-releasebuild") startingAtRow:7 align:GUI_ALIGN_LEFT];
    1251             :                         [gui setColor:[OOColor orangeColor] forRow:7];
    1252             :                         [gui setColor:[OOColor orangeColor] forRow:8];
    1253             : #endif
    1254             :                         NSString *path = [self extractionBasePathForIdentifier:identifier andVersion:version];
    1255             :                         if ([[NSFileManager defaultManager] fileExistsAtPath:path])
    1256             :                         {
    1257             :                                 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-extract-@-already-exists"), path]
    1258             :                                   startingAtRow:10 align:GUI_ALIGN_LEFT];
    1259             :                                 startRow = OXZ_GUI_ROW_CANCEL;
    1260             :                                 [gui setText:DESC(@"oolite-oxzmanager-extract-unavailable") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1261             :                                 [gui setColor:[OOColor grayColor] forRow:OXZ_GUI_ROW_PROCEED];
    1262             :                         }
    1263             :                         else
    1264             :                         {
    1265             :                                 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-extract-to-@"), path]
    1266             :                                   startingAtRow:10 align:GUI_ALIGN_LEFT];
    1267             :                                 startRow = OXZ_GUI_ROW_PROCEED;
    1268             :                                 [gui setText:DESC(@"oolite-oxzmanager-extract-proceed") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1269             :                                 [gui setKey:@"_PROCEED" forRow:OXZ_GUI_ROW_PROCEED];
    1270             : 
    1271             :                         }
    1272             :                         [gui setText:DESC(@"oolite-oxzmanager-extract-cancel") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
    1273             :                         [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
    1274             : 
    1275             :                 }       
    1276             :                 break;
    1277             :         case OXZ_STATE_EXTRACTDONE:
    1278             :                 [gui addLongText:_progressStatus startingAtRow:1 align:GUI_ALIGN_LEFT];
    1279             :                 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1280             :                 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
    1281             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1282             :                 break;
    1283             : 
    1284             :         }
    1285             : 
    1286             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL)
    1287             :         {
    1288             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-install")];
    1289             :                 [self setFilteredList:[self applyCurrentFilter:_oxzList]];
    1290             :                 startRow = [self showInstallOptions];
    1291             :         }
    1292             :         else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    1293             :         {
    1294             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-installed")];
    1295             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    1296             :                 startRow = [self showInstallOptions];
    1297             :         }
    1298             :         else if (_interfaceState == OXZ_STATE_PICK_REMOVE)
    1299             :         {
    1300             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-remove")];
    1301             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    1302             :                 startRow = [self showRemoveOptions];
    1303             :         }
    1304             : 
    1305             : 
    1306             :         if (_changesMade)
    1307             :         {
    1308             :                 [gui setText:DESC(@"oolite-oxzmanager-exit-restart") forRow:OXZ_GUI_ROW_EXIT align:GUI_ALIGN_CENTER];
    1309             :         }
    1310             :         else
    1311             :         {
    1312             :                 [gui setText:DESC(@"oolite-oxzmanager-exit") forRow:OXZ_GUI_ROW_EXIT align:GUI_ALIGN_CENTER];
    1313             :         }
    1314             :         [gui setKey:@"_EXIT" forRow:OXZ_GUI_ROW_EXIT];
    1315             :         [gui setSelectableRange:NSMakeRange(startRow,2+(OXZ_GUI_ROW_EXIT-startRow))];
    1316             :         if (startRow < OXZ_GUI_ROW_INSTALL)
    1317             :         {
    1318             :                 [gui setSelectedRow:OXZ_GUI_ROW_INSTALL];
    1319             :         }
    1320             :         else if (_interfaceState == OXZ_STATE_NODATA)
    1321             :         {
    1322             :                 [gui setSelectedRow:OXZ_GUI_ROW_UPDATE];
    1323             :         }
    1324             :         else
    1325             :         {
    1326             :                 [gui setSelectedRow:startRow];
    1327             :         }
    1328             :         
    1329             : }
    1330             : 
    1331             : 
    1332             : - (BOOL) isRestarting
    1333             : {
    1334             :         // for the restart
    1335             :         if (EXPECT_NOT(_interfaceState == OXZ_STATE_RESTARTING))
    1336             :         {
    1337             :                 // Rebuilds OXP search
    1338             :                 [ResourceManager reset];
    1339             :                 [UNIVERSE reinitAndShowDemo:YES];
    1340             :                 _changesMade = NO;
    1341             :                 _interfaceState = OXZ_STATE_MAIN;
    1342             :                 _downloadStatus = OXZ_DOWNLOAD_NONE; // clear error state
    1343             :                 return YES;
    1344             :         }
    1345             :         else
    1346             :         {
    1347             :                 return NO;
    1348             :         }
    1349             : }
    1350             : 
    1351             : 
    1352             : - (void) processSelection
    1353             : {
    1354             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1355             :         OOGUIRow selection = [gui selectedRow];
    1356             : 
    1357             :         if (selection == OXZ_GUI_ROW_EXIT)
    1358             :         {
    1359             :                 [self cancelUpdate]; // doesn't hurt if no update in progress
    1360             :                 [_dependencyStack removeAllObjects]; // cleanup
    1361             :                 _downloadAllDependencies = NO;
    1362             :                 _downloadStatus = OXZ_DOWNLOAD_NONE; // clear error state
    1363             :                 if (_changesMade)
    1364             :                 {
    1365             :                         _interfaceState = OXZ_STATE_RESTARTING;
    1366             :                 }
    1367             :                 else
    1368             :                 {
    1369             :                         [PLAYER setGuiToIntroFirstGo:YES];
    1370             :                         if (_oxzList != nil)
    1371             :                         {
    1372             :                                 _interfaceState = OXZ_STATE_MAIN;
    1373             :                         }
    1374             :                         else
    1375             :                         {
    1376             :                                 _interfaceState = OXZ_STATE_NODATA;
    1377             :                         }
    1378             :                         return;
    1379             :                 }
    1380             :         }
    1381             :         else if (selection == OXZ_GUI_ROW_UPDATE) // also == _CANCEL
    1382             :         {
    1383             :                 if (_interfaceState == OXZ_STATE_REMOVING)
    1384             :                 {
    1385             :                         _interfaceState = OXZ_STATE_PICK_REMOVE;
    1386             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
    1387             :                 }
    1388             :                 else if (_interfaceState == OXZ_STATE_TASKDONE || _interfaceState == OXZ_STATE_DEPENDENCIES)
    1389             :                 {
    1390             :                         [_dependencyStack removeAllObjects];
    1391             :                         _downloadAllDependencies = NO;
    1392             :                         _interfaceState = OXZ_STATE_PICK_INSTALL;
    1393             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
    1394             :                 }
    1395             :                 else if (_interfaceState == OXZ_STATE_EXTRACTDONE)
    1396             :                 {
    1397             :                         [_dependencyStack removeAllObjects];
    1398             :                         _downloadAllDependencies = NO;
    1399             :                         _interfaceState = OXZ_STATE_PICK_INSTALLED;
    1400             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
    1401             :                 }
    1402             :                 else if (_interfaceState == OXZ_STATE_INSTALLING || _interfaceState == OXZ_STATE_UPDATING)
    1403             :                 {
    1404             :                         [self cancelUpdate]; // sets interface state and download status
    1405             :                 }
    1406             :                 else if (_interfaceState == OXZ_STATE_EXTRACT)
    1407             :                 {
    1408             :                         _interfaceState = OXZ_STATE_MAIN;
    1409             :                 }
    1410             :                 else
    1411             :                 {
    1412             :                         [self updateManifests];
    1413             :                 }
    1414             :         }
    1415             :         else if (selection == OXZ_GUI_ROW_INSTALL)
    1416             :         {
    1417             :                 _interfaceState = OXZ_STATE_PICK_INSTALL;
    1418             :         }
    1419             :         else if (selection == OXZ_GUI_ROW_INSTALLED)
    1420             :         {
    1421             :                 if (_interfaceState == OXZ_STATE_DEPENDENCIES) // also == _PROCEED_ALL
    1422             :                 {
    1423             :                         _downloadAllDependencies = YES;
    1424             :                         [self installOXZ:_item];
    1425             :                 }
    1426             :                 else 
    1427             :                 {
    1428             :                         _interfaceState = OXZ_STATE_PICK_INSTALLED;
    1429             :                 }
    1430             :         }
    1431             :         else if (selection == OXZ_GUI_ROW_REMOVE) // also == _PROCEED
    1432             :         {
    1433             :                 if (_interfaceState == OXZ_STATE_DEPENDENCIES)
    1434             :                 {
    1435             :                         [self installOXZ:_item];
    1436             :                 }
    1437             :                 else if (_interfaceState == OXZ_STATE_NODATA)
    1438             :                 {
    1439             :                         _interfaceState = OXZ_STATE_MAIN;
    1440             :                 }
    1441             :                 else if (_interfaceState == OXZ_STATE_EXTRACT)
    1442             :                 {
    1443             :                         [self setProgressStatus:[self extractOXZ:_item]];
    1444             :                         _interfaceState = OXZ_STATE_EXTRACTDONE;
    1445             :                 }
    1446             :                 else
    1447             :                 {
    1448             :                         _interfaceState = OXZ_STATE_PICK_REMOVE;
    1449             :                 }
    1450             :         }
    1451             :         else if (selection == OXZ_GUI_ROW_UPDATE_ALL)
    1452             :         {
    1453             :                 OOLog(kOOOXZDebugLog, @"%@", @"Trying to update all managed OXPs");
    1454             :                 [self updateAllOXZ];
    1455             :         }
    1456             :         else if (selection == OXZ_GUI_ROW_LISTPREV)
    1457             :         {
    1458             :                 [self processOptionsPrev];
    1459             :                 return;
    1460             :         }
    1461             :         else if (selection == OXZ_GUI_ROW_LISTNEXT)
    1462             :         {
    1463             :                 [self processOptionsNext];
    1464             :                 return;
    1465             :         }
    1466             :         else
    1467             :         {
    1468             :                 NSUInteger item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
    1469             :                 if (_interfaceState == OXZ_STATE_PICK_REMOVE)
    1470             :                 {
    1471             :                         [self removeOXZ:item];
    1472             :                 }
    1473             :                 else if (_interfaceState == OXZ_STATE_PICK_INSTALL)
    1474             :                 {
    1475             :                         OOLog(kOOOXZDebugLog, @"Trying to install index %lu", (unsigned long)item);
    1476             :                         [self installOXZ:item];
    1477             :                 }
    1478             :                 else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    1479             :                 {
    1480             :                         OOLog(kOOOXZDebugLog, @"Trying to install index %lu", (unsigned long)item);
    1481             :                         [self installOXZ:item];
    1482             :                 }
    1483             : 
    1484             :         }
    1485             : 
    1486             :         [self gui]; // update GUI
    1487             : }
    1488             : 
    1489             : 
    1490             : - (BOOL) isAcceptingTextInput
    1491             : {
    1492             :         return (_interfaceState == OXZ_STATE_SETFILTER);
    1493             : }
    1494             : 
    1495             : 
    1496             : - (BOOL) isAcceptingGUIInput
    1497             : {
    1498             :         return !_interfaceShowingOXZDetail;
    1499             : }
    1500             : 
    1501             : 
    1502             : - (void) processTextInput:(NSString *)input
    1503             : {
    1504             :         if ([self validateFilter:input])
    1505             :         {
    1506             :                 if ([input length] > 0)
    1507             :                 {
    1508             :                         [self setFilter:input];
    1509             :                 } // else keep previous filter
    1510             :                 _interfaceState = OXZ_STATE_PICK_INSTALL;
    1511             :                 [self gui];
    1512             :         }
    1513             :         // else nothing
    1514             : }
    1515             : 
    1516             : 
    1517             : - (void) refreshTextInput:(NSString *)input
    1518             : {
    1519             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1520             :         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-text-prompt-@"), input] forRow:OXZ_GUI_ROW_INPUT align:GUI_ALIGN_LEFT];
    1521             :         if ([self validateFilter:input])
    1522             :         {
    1523             :                 [gui setColor:[OOColor cyanColor] forRow:OXZ_GUI_ROW_INPUT];
    1524             :         }
    1525             :         else
    1526             :         {
    1527             :                 [gui setColor:[OOColor orangeColor] forRow:OXZ_GUI_ROW_INPUT];
    1528             :         }
    1529             : }
    1530             : 
    1531             : 
    1532             : - (void) processFilterKey
    1533             : {
    1534             :         if (_interfaceShowingOXZDetail)
    1535             :         {
    1536             :                 _interfaceShowingOXZDetail = NO;
    1537             :         }
    1538             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_MAIN)
    1539             :         {
    1540             :                 _interfaceState = OXZ_STATE_SETFILTER;
    1541             :                 [[UNIVERSE gameView] resetTypedString];
    1542             :                 [self gui];
    1543             :         }
    1544             :         // else this key does nothing
    1545             : }
    1546             : 
    1547             : 
    1548             : - (void) processShowInfoKey
    1549             : {
    1550             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE)
    1551             :         {
    1552             :                 GuiDisplayGen   *gui = [UNIVERSE gui];
    1553             : 
    1554             :                 if (_interfaceShowingOXZDetail)
    1555             :                 {
    1556             :                         _interfaceShowingOXZDetail = NO;
    1557             :                         [self gui]; // restore screen
    1558             :                         // reset list selection position
    1559             :                         [gui setSelectedRow:(_item - _offset + OXZ_GUI_ROW_LISTSTART)];
    1560             :                         // and do the GUI again with the correct positions
    1561             :                         [self showOptionsUpdate]; // restore screen
    1562             :                 }
    1563             :                 else
    1564             :                 {
    1565             :                         OOGUIRow selection = [gui selectedRow];
    1566             :                         
    1567             :                         if (selection < OXZ_GUI_ROW_LISTSTART || selection >= OXZ_GUI_ROW_LISTSTART + OXZ_GUI_NUM_LISTROWS)
    1568             :                         {
    1569             :                                 // not on an OXZ
    1570             :                                 return;
    1571             :                         }
    1572             : 
    1573             : 
    1574             :                         _item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
    1575             : 
    1576             :                         NSDictionary *manifest = [_filteredList oo_dictionaryAtIndex:_item];
    1577             :                         _interfaceShowingOXZDetail = YES;
    1578             : 
    1579             :                         [gui clearAndKeepBackground:YES];
    1580             :                         [gui setTitle:DESC(@"oolite-oxzmanager-title-infopage")];
    1581             : 
    1582             : // title, version                       
    1583             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-title-@-version-@"),
    1584             :                                                                    [manifest oo_stringForKey:kOOManifestTitle],
    1585             :                                                                    [manifest oo_stringForKey:kOOManifestVersion]]
    1586             :                                   forRow:0 align:GUI_ALIGN_LEFT];
    1587             : 
    1588             : // author
    1589             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-author-@"),
    1590             :                                                                    [manifest oo_stringForKey:kOOManifestAuthor]]
    1591             :                                   forRow:1 align:GUI_ALIGN_LEFT];
    1592             : 
    1593             : // license
    1594             :                         [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-license-@"),
    1595             :                                                                    [manifest oo_stringForKey:kOOManifestLicense]]
    1596             :                                   startingAtRow:2 align:GUI_ALIGN_LEFT];
    1597             : // tags
    1598             :                         
    1599             :                         [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-tags-@"),[[manifest oo_arrayForKey:kOOManifestTags] componentsJoinedByString: @", "]]
    1600             :                                   startingAtRow:4  align:GUI_ALIGN_LEFT];
    1601             : // description
    1602             :                         [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-description-@"),[manifest oo_stringForKey:kOOManifestDescription]]
    1603             :                                   startingAtRow:7  align:GUI_ALIGN_LEFT];
    1604             : 
    1605             : // infoURL              
    1606             :                         NSString *infoURLString = [manifest oo_stringForKey:kOOManifestInformationURL];
    1607             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-infourl-@"),
    1608             :                                                                    infoURLString]
    1609             :                                   forRow:25 align:GUI_ALIGN_LEFT];
    1610             :                         // copy url info text to clipboard automatically once we are in the oxz info page
    1611             :                         [[UNIVERSE gameView] stringToClipboard:infoURLString];    
    1612             :                                   
    1613             : // instructions
    1614             :                         [gui setText:OOExpand(DESC(@"oolite-oxzmanager-infopage-return")) forRow:27 align:GUI_ALIGN_CENTER];
    1615             :                         [gui setColor:[OOColor greenColor] forRow:27];
    1616             : 
    1617             :                 }
    1618             :         }
    1619             : }
    1620             : 
    1621             : 
    1622             : - (void) processExtractKey
    1623             : {
    1624             :         // TODO: Extraction functionality - converts an installed OXZ to
    1625             :         // an OXP in the main AddOns folder if it's safe to do so.
    1626             :         if (!_interfaceShowingOXZDetail && (_interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE))
    1627             :         {
    1628             :                 GuiDisplayGen   *gui = [UNIVERSE gui];
    1629             :                 OOGUIRow selection = [gui selectedRow];
    1630             :                 
    1631             :                 if (selection < OXZ_GUI_ROW_LISTSTART || selection >= OXZ_GUI_ROW_LISTSTART + OXZ_GUI_NUM_LISTROWS)
    1632             :                 {
    1633             :                         // not on an OXZ
    1634             :                         return;
    1635             :                 }
    1636             :                 
    1637             :                 _item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
    1638             :                 _interfaceState = OXZ_STATE_EXTRACT;
    1639             :                 [self gui];
    1640             :         }
    1641             : }
    1642             : 
    1643             : 
    1644           0 : - (BOOL) installOXZ:(NSUInteger)item 
    1645             : {
    1646             :         NSArray *picklist = _filteredList;
    1647             : 
    1648             :         if ([picklist count] <= item)
    1649             :         {
    1650             :                 return NO;
    1651             :         }
    1652             :         NSDictionary *manifest = [picklist objectAtIndex:item];
    1653             :         _item = item;
    1654             : 
    1655             :         if ([self installableState:manifest] >= OXZ_UNINSTALLABLE_ALREADY)
    1656             :         {
    1657             :                 OOLog(kOOOXZDebugLog,@"Cannot install %@",manifest);
    1658             :                 // can't be installed on this version of Oolite, or already is installed
    1659             :                 return NO;
    1660             :         }
    1661             :         NSString *url = [manifest objectForKey:kOOManifestDownloadURL];
    1662             :         if (url == nil)
    1663             :         {
    1664             :                 OOLog(kOOOXZErrorLog, @"%@", @"Manifest does not have a download URL - cannot install");
    1665             :                 return NO;
    1666             :         }
    1667             :         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    1668             :         if (_downloadStatus != OXZ_DOWNLOAD_NONE)
    1669             :         {
    1670             :                 return NO;
    1671             :         }
    1672             :         _downloadStatus = OXZ_DOWNLOAD_STARTED;
    1673             :         _interfaceState = OXZ_STATE_INSTALLING;
    1674             :         
    1675             :         [self setProgressStatus:@""];
    1676             :         return [self beginDownload:request];
    1677             : }
    1678             : 
    1679             : 
    1680           0 : - (BOOL) updateAllOXZ
    1681             : {
    1682             :         [_dependencyStack removeAllObjects];
    1683             :         _downloadAllDependencies = YES;
    1684             :         [self setFilteredList:_oxzList];
    1685             :         NSDictionary *manifest = nil;
    1686             : 
    1687             :         foreach (manifest,_oxzList)
    1688             :         {
    1689             :                 if ([self installableState:manifest] == OXZ_INSTALLABLE_UPDATE)
    1690             :                 {
    1691             :                         OOLog(kOOOXZDebugLog, @"Queuing in for update: %@", manifest);
    1692             :                         [_dependencyStack addObject:manifest];
    1693             :                 }
    1694             :         }
    1695             :         NSDictionary *first = [_dependencyStack anyObject];
    1696             :         NSString* identifier = [first oo_stringForKey:kOOManifestRelationIdentifier];
    1697             :         NSUInteger item = NSUIntegerMax;
    1698             :         NSDictionary *availableDownload = nil;
    1699             :         foreach (availableDownload, _oxzList)
    1700             :         {
    1701             :                 if ([[availableDownload oo_stringForKey:kOOManifestIdentifier] isEqualToString:identifier])
    1702             :                 {
    1703             :                         item = [_oxzList indexOfObject:availableDownload];
    1704             :                         break;
    1705             :                 }
    1706             :         }
    1707             :         return [self installOXZ:item];
    1708             : }
    1709             : 
    1710             : 
    1711           0 : - (NSArray *) installOptions
    1712             : {
    1713             :         NSUInteger start = _offset;
    1714             :         if (start >= [_filteredList count])
    1715             :         {
    1716             :                 start = 0;
    1717             :                 _offset = 0;
    1718             :         }
    1719             :         NSUInteger end = start + OXZ_GUI_NUM_LISTROWS;
    1720             :         if (end > [_filteredList count])
    1721             :         {
    1722             :                 end = [_filteredList count];
    1723             :         }
    1724             :         return [_filteredList subarrayWithRange:NSMakeRange(start,end-start)];
    1725             : }
    1726             : 
    1727             : 
    1728             : - (OOGUIRow) showInstallOptions
    1729             : {
    1730             :         // shows the current installation options page
    1731             :         OOGUIRow startRow = OXZ_GUI_ROW_LISTPREV;
    1732             :         NSArray *options = [self installOptions];
    1733             :         NSUInteger optCount = [_filteredList count];
    1734             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1735             :         OOGUITabSettings tab_stops;
    1736             :         tab_stops[0] = 0;
    1737             :         tab_stops[1] = 100;
    1738             :         tab_stops[2] = 320;
    1739             :         tab_stops[3] = 400;
    1740             :         [gui setTabStops:tab_stops];
    1741             :         
    1742             : 
    1743             :         [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-heading-category"),
    1744             :                                                    DESC(@"oolite-oxzmanager-heading-title"), 
    1745             :                                                    DESC(@"oolite-oxzmanager-heading-installed"), 
    1746             :                                                    DESC(@"oolite-oxzmanager-heading-downloadable"), 
    1747             :                                                                 nil] forRow:OXZ_GUI_ROW_LISTHEAD];
    1748             : 
    1749             :         if (_offset > 0)
    1750             :         {
    1751             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTPREV];
    1752             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"",@"",@" <-- ", nil] forRow:OXZ_GUI_ROW_LISTPREV];
    1753             :                 [gui setKey:@"_BACK" forRow:OXZ_GUI_ROW_LISTPREV];
    1754             :         }
    1755             :         else
    1756             :         {
    1757             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
    1758             :                 {
    1759             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    1760             :                 }
    1761             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTPREV align:GUI_ALIGN_LEFT];
    1762             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTPREV];
    1763             :         }
    1764             :         if (_offset + 10 < optCount)
    1765             :         {
    1766             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTNEXT];
    1767             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"",@"",@" --> ", nil] forRow:OXZ_GUI_ROW_LISTNEXT];
    1768             :                 [gui setKey:@"_NEXT" forRow:OXZ_GUI_ROW_LISTNEXT];
    1769             :         }
    1770             :         else
    1771             :         {
    1772             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
    1773             :                 {
    1774             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    1775             :                 }
    1776             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTNEXT align:GUI_ALIGN_LEFT];
    1777             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTNEXT];
    1778             :         }
    1779             : 
    1780             :         // clear any previous longtext
    1781             :         for (NSUInteger i = OXZ_GUI_ROW_LISTSTATUS; i < OXZ_GUI_ROW_INSTALL-1; i++)
    1782             :         {
    1783             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    1784             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    1785             :         }
    1786             :         // and any previous listed entries
    1787             :         for (NSUInteger i = OXZ_GUI_ROW_LISTSTART; i < OXZ_GUI_ROW_LISTNEXT; i++)
    1788             :         {
    1789             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    1790             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    1791             :         }
    1792             : 
    1793             :         OOGUIRow row = OXZ_GUI_ROW_LISTSTART;
    1794             :         NSDictionary *manifest = nil;
    1795             :         BOOL oxzLineSelected = NO;
    1796             : 
    1797             :         foreach (manifest, options)
    1798             :         {
    1799             :                 NSDictionary *installed = [ResourceManager manifestForIdentifier:[manifest oo_stringForKey:kOOManifestIdentifier]];
    1800             :                 NSString *localPath = [[[self installPath] stringByAppendingPathComponent:[manifest oo_stringForKey:kOOManifestIdentifier]] stringByAppendingPathExtension:@"oxz"];
    1801             :                 if (installed == nil)
    1802             :                 {
    1803             :                         // check that there's not one just been downloaded
    1804             :                         installed = OODictionaryFromFile([localPath stringByAppendingPathComponent:@"manifest.plist"]);
    1805             :                 }
    1806             :                 else
    1807             :                 {
    1808             :                         // check for a more recent download
    1809             :                         if ([[NSFileManager defaultManager] fileExistsAtPath:localPath])
    1810             :                         {
    1811             :                                 
    1812             :                                 installed = OODictionaryFromFile([localPath stringByAppendingPathComponent:@"manifest.plist"]);
    1813             :                         }
    1814             :                         else
    1815             :                         {
    1816             :                                 // check if this was a managed OXZ which has been deleted
    1817             :                                 if ([[installed oo_stringForKey:kOOManifestFilePath] hasPrefix:[self installPath]])
    1818             :                                 {
    1819             :                                         installed = nil;
    1820             :                                 }
    1821             :                         }
    1822             :                 }
    1823             : 
    1824             :                 NSString *installedVersion = DESC(@"oolite-oxzmanager-version-none");
    1825             :                 if (installed != nil)
    1826             :                 {
    1827             :                         installedVersion = [installed oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-version-none")];
    1828             :                 }
    1829             : 
    1830             :                 /* If the filter is in use, the available_version key will
    1831             :                  * contain the version which can be downloaded. */
    1832             :                 [gui setArray:[NSArray arrayWithObjects:
    1833             :                          [manifest oo_stringForKey:kOOManifestCategory defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    1834             :                          [manifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    1835             :                          installedVersion,
    1836             :                          [manifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[manifest oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-version-none")]],
    1837             :                   nil] forRow:row];
    1838             : 
    1839             :                 [gui setKey:[manifest oo_stringForKey:kOOManifestIdentifier] forRow:row];
    1840             :                 /* yellow for installable, orange for dependency issues, grey and unselectable for version issues, white and unselectable for already installed (manually or otherwise) at the current version, red and unselectable for already installed manually at a different version. */
    1841             :                 [gui setColor:[self colorForManifest:manifest] forRow:row];
    1842             : 
    1843             :                 if (row == [gui selectedRow])
    1844             :                 {
    1845             :                         oxzLineSelected = YES;
    1846             :                         
    1847             :                         [gui setText:[self installStatusForManifest:manifest] forRow:OXZ_GUI_ROW_LISTSTATUS];
    1848             :                         [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTSTATUS];
    1849             : 
    1850             :                         [gui addLongText:[[[manifest oo_stringForKey:kOOManifestDescription] componentsSeparatedByString:@"\n"] oo_stringAtIndex:0] startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    1851             :                         
    1852             :                         NSString *infoUrl = [manifest oo_stringForKey:kOOManifestInformationURL];
    1853             :                         if (infoUrl != nil)
    1854             :                         {
    1855             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-url"),infoUrl,nil] forRow:OXZ_GUI_ROW_LISTINFO1];
    1856             :                         }
    1857             :                         NSUInteger size = [manifest oo_unsignedIntForKey:kOOManifestFileSize defaultValue:0];
    1858             :                         NSString *updatedDesc = nil;
    1859             : 
    1860             :                         NSUInteger timestamp = [manifest oo_unsignedIntegerForKey:kOOManifestUploadDate defaultValue:0];
    1861             :                         if (timestamp > 0)
    1862             :                         {
    1863             :                                 // list of installable OXZs
    1864             :                                 NSDate *updated = [NSDate dateWithTimeIntervalSince1970:timestamp];
    1865             :                         
    1866             :                                 //keep only the first part of the date string description, which should be in YYYY-MM-DD format
    1867             :                                 updatedDesc = [[[updated description] componentsSeparatedByString:@" "] oo_stringAtIndex:0];
    1868             :                                 
    1869             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-size"),[self humanSize:size],DESC(@"oolite-oxzmanager-infoline-date"),updatedDesc,nil] forRow:OXZ_GUI_ROW_LISTINFO2];
    1870             :                         } 
    1871             :                         else if (size > 0)
    1872             :                         {
    1873             :                                 // list of installed/removable OXZs
    1874             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-size"),[self humanSize:size],nil] forRow:OXZ_GUI_ROW_LISTINFO2];
    1875             :                         }
    1876             :                         
    1877             : 
    1878             :                 }
    1879             :                 
    1880             : 
    1881             :                 row++;
    1882             :         }
    1883             : 
    1884             :         if (!oxzLineSelected)
    1885             :         {
    1886             :                 if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    1887             :                 {
    1888             :                         // installeD
    1889             :                         [gui addLongText:OOExpand(DESC(@"oolite-oxzmanager-installed-nonepicked")) startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    1890             :                 }
    1891             :                 else
    1892             :                 {
    1893             :                         // installeR
    1894             :                         [gui addLongText:OOExpand(DESC(@"oolite-oxzmanager-installer-nonepicked")) startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    1895             :                 }
    1896             :                 
    1897             :         }
    1898             : 
    1899             : 
    1900             :         return startRow;
    1901             : }
    1902             : 
    1903             : 
    1904           0 : - (BOOL) removeOXZ:(NSUInteger)item
    1905             : {
    1906             :         NSArray *remList = _filteredList;
    1907             :         if ([remList count] <= item)
    1908             :         {
    1909             :                 OOLog(kOOOXZDebugLog, @"Unable to remove item %lu as only %lu in list", (unsigned long)item, (unsigned long)[remList count]);
    1910             :                 return NO;
    1911             :         }
    1912             :         NSString *filename = [[remList objectAtIndex:item] oo_stringForKey:kOOManifestFilePath];
    1913             :         if (filename == nil)
    1914             :         {
    1915             :                 OOLog(kOOOXZDebugLog, @"Unable to remove item %lu as filename not found", (unsigned long)item);
    1916             :                 return NO;
    1917             :         }
    1918             : 
    1919             :         if (![[NSFileManager defaultManager] oo_removeItemAtPath:filename])
    1920             :         {
    1921             :                 OOLog(kOOOXZErrorLog, @"Unable to remove file %@", filename);
    1922             :                 return NO;
    1923             :         }
    1924             :         _changesMade = YES;
    1925             :         DESTROY(_managedList); // will need updating
    1926             :         _interfaceState = OXZ_STATE_REMOVING;
    1927             :         [self gui];
    1928             :         return YES;
    1929             : }
    1930             : 
    1931             : 
    1932           0 : - (NSArray *) removeOptions
    1933             : {
    1934             :         NSArray *remList = _filteredList;
    1935             :         if ([remList count] == 0)
    1936             :         {
    1937             :                 return nil;
    1938             :         }
    1939             :         NSUInteger start = _offset;
    1940             :         if (start >= [remList count])
    1941             :         {
    1942             :                 start = 0;
    1943             :                 _offset = 0;
    1944             :         }
    1945             :         NSUInteger end = start + OXZ_GUI_NUM_LISTROWS;
    1946             :         if (end > [remList count])
    1947             :         {
    1948             :                 end = [remList count];
    1949             :         }
    1950             :         return [remList subarrayWithRange:NSMakeRange(start,end-start)];
    1951             : }
    1952             : 
    1953             : 
    1954             : - (OOGUIRow) showRemoveOptions
    1955             : {
    1956             :         // shows the current installation options page
    1957             :         OOGUIRow startRow = OXZ_GUI_ROW_LISTPREV;
    1958             :         NSArray *options = [self removeOptions];
    1959             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1960             :         if (options == nil)
    1961             :         {
    1962             :                 [gui addLongText:DESC(@"oolite-oxzmanager-nothing-removable") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1963             :                 return startRow;
    1964             :         }
    1965             : 
    1966             :         OOGUITabSettings tab_stops;
    1967             :         tab_stops[0] = 0;
    1968             :         tab_stops[1] = 100;
    1969             :         tab_stops[2] = 400;
    1970             :         [gui setTabStops:tab_stops];
    1971             :         
    1972             :         [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-heading-category"),
    1973             :                                                    DESC(@"oolite-oxzmanager-heading-title"), 
    1974             :                                                    DESC(@"oolite-oxzmanager-heading-version"), 
    1975             :                                                                 nil] forRow:OXZ_GUI_ROW_LISTHEAD];
    1976             :         if (_offset > 0)
    1977             :         {
    1978             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTPREV];
    1979             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"",@" <-- ", nil] forRow:OXZ_GUI_ROW_LISTPREV];
    1980             :                 [gui setKey:@"_BACK" forRow:OXZ_GUI_ROW_LISTPREV];
    1981             :         }
    1982             :         else
    1983             :         {
    1984             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
    1985             :                 {
    1986             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    1987             :                 }
    1988             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTPREV align:GUI_ALIGN_LEFT];
    1989             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTPREV];
    1990             :         }
    1991             :         if (_offset + OXZ_GUI_NUM_LISTROWS < [[self managedOXZs] count])
    1992             :         {
    1993             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTNEXT];
    1994             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"",@" --> ", nil] forRow:OXZ_GUI_ROW_LISTNEXT];
    1995             :                 [gui setKey:@"_NEXT" forRow:OXZ_GUI_ROW_LISTNEXT];
    1996             :         }
    1997             :         else
    1998             :         {
    1999             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
    2000             :                 {
    2001             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    2002             :                 }
    2003             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTNEXT align:GUI_ALIGN_LEFT];
    2004             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTNEXT];
    2005             :         }
    2006             : 
    2007             :         // clear any previous longtext
    2008             :         for (NSUInteger i = OXZ_GUI_ROW_LISTDESC; i < OXZ_GUI_ROW_INSTALL-1; i++)
    2009             :         {
    2010             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    2011             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    2012             :         }
    2013             :         // and any previous listed entries
    2014             :         for (NSUInteger i = OXZ_GUI_ROW_LISTSTART; i < OXZ_GUI_ROW_LISTNEXT; i++)
    2015             :         {
    2016             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    2017             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    2018             :         }
    2019             : 
    2020             : 
    2021             :         OOGUIRow row = OXZ_GUI_ROW_LISTSTART;
    2022             :         NSDictionary *manifest = nil;
    2023             :         BOOL oxzSelected = NO;
    2024             : 
    2025             :         foreach (manifest, options)
    2026             :         {
    2027             : 
    2028             :                 [gui setArray:[NSArray arrayWithObjects:
    2029             :                                                                    [manifest oo_stringForKey:kOOManifestCategory defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    2030             :                                                            [manifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    2031             :                                                            [manifest oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    2032             :                                                                         nil] forRow:row];
    2033             :                 NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier];
    2034             :                 [gui setKey:identifier forRow:row];
    2035             :                 
    2036             :                 [gui setColor:[self colorForManifest:manifest] forRow:row];
    2037             :                 
    2038             :                 if (row == [gui selectedRow])
    2039             :                 {
    2040             :                         [gui setText:[self installStatusForManifest:manifest] forRow:OXZ_GUI_ROW_LISTSTATUS];
    2041             :                         [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTSTATUS];
    2042             : 
    2043             :                         [gui addLongText:[[[manifest oo_stringForKey:kOOManifestDescription] componentsSeparatedByString:@"\n"] oo_stringAtIndex:0] startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    2044             :                         
    2045             :                         oxzSelected = YES;
    2046             :                 }
    2047             :                 row++;
    2048             :         }
    2049             : 
    2050             :         if (!oxzSelected)
    2051             :         {
    2052             :                 [gui addLongText:DESC(@"oolite-oxzmanager-remover-nonepicked") startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    2053             :         }
    2054             : 
    2055             :         return startRow;        
    2056             : }
    2057             : 
    2058             : 
    2059             : - (void) showOptionsUpdate
    2060             : {
    2061             : 
    2062             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL)
    2063             :         {
    2064             :                 [self setFilteredList:[self applyCurrentFilter:_oxzList]];
    2065             :                 [self showInstallOptions];
    2066             :         }
    2067             :         else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    2068             :         {
    2069             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    2070             :                 [self showInstallOptions];
    2071             :         }
    2072             :         else if (_interfaceState == OXZ_STATE_PICK_REMOVE)
    2073             :         {
    2074             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    2075             :                 [self showRemoveOptions];
    2076             :         }
    2077             :         // else nothing necessary
    2078             : }
    2079             : 
    2080             : 
    2081             : - (void) showOptionsPrev
    2082             : {
    2083             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    2084             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_PICK_INSTALLED)
    2085             :         {
    2086             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
    2087             :                 {
    2088             :                         [self processSelection];
    2089             :                 }
    2090             :         }
    2091             : }
    2092             : 
    2093             : 
    2094             : - (void) processOptionsPrev
    2095             : {
    2096             :         if (_offset < OXZ_GUI_NUM_LISTROWS)  
    2097             :         {
    2098             :                 _offset = 0;
    2099             :         }
    2100             :         else
    2101             :         {
    2102             :                 _offset -= OXZ_GUI_NUM_LISTROWS;
    2103             :         }
    2104             :         [self showOptionsUpdate];
    2105             : }
    2106             : 
    2107             : 
    2108             : - (void) processOptionsNext
    2109             : {
    2110             :         if (_offset + OXZ_GUI_NUM_LISTROWS < [_filteredList count])
    2111             :         {
    2112             :                 _offset += OXZ_GUI_NUM_LISTROWS;
    2113             :         }
    2114             :         [self showOptionsUpdate];
    2115             :         return;
    2116             : }
    2117             : 
    2118             : 
    2119             : - (void) showOptionsNext
    2120             : {
    2121             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    2122             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_PICK_INSTALLED)
    2123             :         {
    2124             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
    2125             :                 {
    2126             :                         [self processSelection];
    2127             :                 }
    2128             :         }
    2129             : }
    2130             : 
    2131             : 
    2132           0 : - (NSString *) extractOXZ:(NSUInteger)item
    2133             : {
    2134             :         NSFileManager *fmgr                     = [NSFileManager defaultManager];
    2135             :         NSMutableString *extractionLog  = [[NSMutableString alloc] init];
    2136             :         NSDictionary *manifest                  = [_filteredList oo_dictionaryAtIndex:item];
    2137             :         NSString *version                               = [manifest oo_stringForKey:kOOManifestVersion];
    2138             :         NSString *identifier                    = [manifest oo_stringForKey:kOOManifestIdentifier];
    2139             :         NSString *path                                  = [self extractionBasePathForIdentifier:identifier andVersion:version];
    2140             : 
    2141             :         // OXZ errors should really never happen unless someone is messing
    2142             :         // directly with the managed folder while Oolite is running, but
    2143             :         // it's possible.
    2144             : 
    2145             :         NSString *oxzfile = [manifest oo_stringForKey:kOOManifestFilePath];
    2146             :         if (![fmgr fileExistsAtPath:oxzfile])
    2147             :         {
    2148             :                 OOLog(kOOOXZErrorLog,@"OXZ %@ could not be found",oxzfile);
    2149             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-no-original")];
    2150             :                 return [extractionLog autorelease];
    2151             :         }
    2152             :         const char* zipname = [oxzfile UTF8String];
    2153             :         unzFile uf = NULL;
    2154             :         uf = unzOpen64(zipname);
    2155             :         if (uf == NULL)
    2156             :         {
    2157             :                 OOLog(kOOOXZErrorLog,@"Could not open .oxz at %@ as zip file",path);
    2158             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-bad-original")];
    2159             :                 return [extractionLog autorelease];
    2160             :         }       
    2161             : 
    2162             :         if ([fmgr fileExistsAtPath:path])
    2163             :         {
    2164             :                 OOLog(kOOOXZErrorLog,@"Path %@ already exists",path);
    2165             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-exists")];
    2166             :                 unzClose(uf);
    2167             :                 return [extractionLog autorelease];
    2168             :         }
    2169             :         if (![fmgr oo_createDirectoryAtPath:path attributes:nil])
    2170             :         {
    2171             :                 OOLog(kOOOXZErrorLog,@"Path %@ could not be created",path);
    2172             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-unmakeable")];
    2173             :                 unzClose(uf);
    2174             :                 return [extractionLog autorelease];
    2175             :         }
    2176             :         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-created")];
    2177             :         NSUInteger counter = 0;
    2178             :         char rawComponentName[512];
    2179             :         BOOL error = NO;
    2180             :         unz_file_info64 file_info = {0};
    2181             :         if (unzGoToFirstFile(uf) == UNZ_OK)
    2182             :         {
    2183             :                 do 
    2184             :                 {
    2185             :                         unzGetCurrentFileInfo64(uf, &file_info,
    2186             :                                                                         rawComponentName, 512,
    2187             :                                                                         NULL, 0,
    2188             :                                                                         NULL, 0);
    2189             :                         NSString *componentName = [NSString stringWithUTF8String:rawComponentName];
    2190             :                         if ([componentName hasSuffix:@"/"])
    2191             :                         {
    2192             :                                 // folder
    2193             :                                 if (![fmgr oo_createDirectoryAtPath:[path stringByAppendingPathComponent:componentName] attributes:nil])
    2194             :                                 {
    2195             :                                         OOLog(kOOOXZErrorLog,@"Subpath %@ could not be created",componentName);
    2196             :                                         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2197             :                                         error = YES;
    2198             :                                         break;
    2199             :                                 }
    2200             :                                 else
    2201             :                                 {
    2202             :                                         OOLog(kOOOXZDebugLog,@"Subpath %@ created OK",componentName);
    2203             :                                 }
    2204             :                         }
    2205             :                         else
    2206             :                         {
    2207             :                                 // file
    2208             :                                 // usually folder must now exist, but just in case...
    2209             :                                 NSString *folder = [[path stringByAppendingPathComponent:componentName] stringByDeletingLastPathComponent];
    2210             :                                 if ([folder length] > 0 && ![fmgr fileExistsAtPath:folder] && ![fmgr oo_createDirectoryAtPath:folder attributes:nil])
    2211             :                                 {
    2212             :                                         OOLog(kOOOXZErrorLog,@"Subpath %@ could not be created",folder);
    2213             :                                         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2214             :                                         error = YES;
    2215             :                                         break;
    2216             :                                 }
    2217             :                                 
    2218             : 
    2219             :                                 // This is less efficient in memory use than just
    2220             :                                 // streaming out of the ZIP file onto disk
    2221             :                                 // but it makes error handling easier
    2222             :                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    2223             :                                 NSData *tmp = [NSData oo_dataWithOXZFile:[oxzfile stringByAppendingPathComponent:componentName]];
    2224             :                                 if (tmp == nil)
    2225             :                                 {
    2226             :                                         OOLog(kOOOXZErrorLog,@"Sub file %@ could not be extracted from the OXZ",componentName);
    2227             :                                         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2228             :                                         error = YES;
    2229             :                                         [pool release];
    2230             :                                         break;
    2231             :                                 }
    2232             :                                 else
    2233             :                                 {
    2234             :                                         if (![tmp writeToFile:[path stringByAppendingPathComponent:componentName] atomically:YES])
    2235             :                                         {
    2236             :                                                 OOLog(kOOOXZErrorLog,@"Sub file %@ could not be created",componentName);
    2237             :                                                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2238             :                                                 error = YES;
    2239             :                                                 [pool release];
    2240             :                                                 break;
    2241             :                                         }
    2242             :                                         else
    2243             :                                         {
    2244             :                                                 ++counter;
    2245             :                                         }
    2246             :                                 }
    2247             :                                 [pool release];
    2248             : 
    2249             :                         }
    2250             :                 }
    2251             :                 while (unzGoToNextFile(uf) == UNZ_OK);
    2252             :         }
    2253             :         unzClose(uf);
    2254             : 
    2255             :         if (!error)
    2256             :         {
    2257             :                 [extractionLog appendFormat:DESC(@"oolite-oxzmanager-extract-log-num-u-extracted"),counter];
    2258             :                 [extractionLog appendFormat:DESC(@"oolite-oxzmanager-extract-log-extracted-to-@"),path];
    2259             :         }
    2260             : 
    2261             :         return [extractionLog autorelease];
    2262             : }
    2263             : 
    2264             : 
    2265             : 
    2266             : 
    2267           0 : - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    2268             : {
    2269             :         _downloadStatus = OXZ_DOWNLOAD_RECEIVING;
    2270             :         OOLog(kOOOXZDebugLog, @"%@", @"Download receiving");
    2271             :         _downloadExpected = [response expectedContentLength];
    2272             :         _downloadProgress = 0;
    2273             :         DESTROY(_fileWriter);
    2274             :         [[NSFileManager defaultManager] createFileAtPath:[self downloadPath] contents:nil attributes:nil];
    2275             :         _fileWriter = [[NSFileHandle fileHandleForWritingAtPath:[self downloadPath]] retain];
    2276             :         if (_fileWriter == nil)
    2277             :         {
    2278             :                 // file system is full or read-only or something
    2279             :                 OOLog(kOOOXZErrorLog, @"%@", @"Unable to create download file");
    2280             :                 [self cancelUpdate];
    2281             :         }
    2282             : }
    2283             : 
    2284             : 
    2285           0 : - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    2286             : {
    2287             :         OOLog(kOOOXZDebugLog,@"Downloaded %lu bytes",[data length]);
    2288             :         [_fileWriter seekToEndOfFile];
    2289             :         [_fileWriter writeData:data];
    2290             :         _downloadProgress += [data length];
    2291             :         [self gui]; // update GUI
    2292             : #if OOLITE_WINDOWS
    2293             :         /* Irritating fix to issue https://github.com/OoliteProject/oolite/issues/95
    2294             :          *
    2295             :          * The problem is that on MINGW, GNUStep makes all socket streams
    2296             :          * blocking, which causes problems with the run loop. Calling this
    2297             :          * method of the run loop forces it to execute all already
    2298             :          * scheduled items with a time in the past, before any more items
    2299             :          * are placed on it, which means that the main game update gets a
    2300             :          * chance to run.
    2301             :          *
    2302             :          * This stops the interface freezing - and Oolite appearing to
    2303             :          * have stopped responding to the OS - when downloading large
    2304             :          * (>20Mb) OXZ files.
    2305             :          *
    2306             :          * CIM 6 July 2014
    2307             :          */
    2308             :         [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
    2309             : #endif
    2310             : }
    2311             : 
    2312             : 
    2313           0 : - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    2314             : {
    2315             :         _downloadStatus = OXZ_DOWNLOAD_COMPLETE;
    2316             :         OOLog(kOOOXZDebugLog, @"%@", @"Download complete");
    2317             :         [_fileWriter synchronizeFile];
    2318             :         [_fileWriter closeFile];
    2319             :         DESTROY(_fileWriter);
    2320             :         DESTROY(_currentDownload);
    2321             :         if (_interfaceState == OXZ_STATE_UPDATING)
    2322             :         {
    2323             :                 if (![self processDownloadedManifests])
    2324             :                 {
    2325             :                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2326             :                 }
    2327             :         }
    2328             :         else if (_interfaceState == OXZ_STATE_INSTALLING)
    2329             :         {
    2330             :                 if (![self processDownloadedOXZ])
    2331             :                 {
    2332             :                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2333             :                 }
    2334             :         }
    2335             :         else
    2336             :         {
    2337             :                 OOLog(kOOOXZErrorLog,@"Error: download completed in unexpected state %d. This is an internal error - please report it.",_interfaceState);
    2338             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2339             :         }
    2340             : }
    2341             : 
    2342             : 
    2343           0 : - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    2344             : {
    2345             :         _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2346             :         OOLog(kOOOXZErrorLog,@"Error downloading file: %@",[error description]);
    2347             :         [_fileWriter closeFile];
    2348             :         DESTROY(_fileWriter);
    2349             :         DESTROY(_currentDownload);
    2350             : }
    2351             : 
    2352             : 
    2353             : 
    2354             : 
    2355             : @end
    2356             : 
    2357             : /* Sort by category, then title, then version - and that should be unique */
    2358           0 : NSComparisonResult oxzSort(id m1, id m2, void *context)
    2359             : {
    2360             :         NSComparisonResult result = [[m1 oo_stringForKey:kOOManifestCategory defaultValue:@"zz"] localizedCompare:[m2 oo_stringForKey:kOOManifestCategory defaultValue:@"zz"]];
    2361             :         if (result == NSOrderedSame)
    2362             :         {
    2363             :                 result = [[m1 oo_stringForKey:kOOManifestTitle defaultValue:@"zz"] localizedCompare:[m2 oo_stringForKey:kOOManifestTitle defaultValue:@"zz"]];
    2364             :                 if (result == NSOrderedSame)
    2365             :                 {
    2366             :                         result = [[m2 oo_stringForKey:kOOManifestVersion defaultValue:@"0"] localizedCompare:[m1 oo_stringForKey:kOOManifestVersion defaultValue:@"0"]];
    2367             :                 }
    2368             :         }
    2369             :         return result;
    2370             : }

Generated by: LCOV version 1.14