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-12-19 09:33:35 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:@"%llu 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             :         [request setHTTPShouldHandleCookies:NO];
     584             :         if (_downloadStatus != OXZ_DOWNLOAD_NONE)
     585             :         {
     586             :                 return NO;
     587             :         }
     588             :         _downloadStatus = OXZ_DOWNLOAD_STARTED;
     589             :         _interfaceState = OXZ_STATE_UPDATING;
     590             :         [self setProgressStatus:@""];
     591             : 
     592             :         return [self beginDownload:request];
     593             : }
     594             : 
     595             : 
     596           0 : - (BOOL) beginDownload:(NSMutableURLRequest *)request
     597             : {
     598             :         NSString *userAgent = [NSString stringWithFormat:@"Oolite/%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]];
     599             :         [request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
     600             :         NSURLConnection *download = [[NSURLConnection alloc] initWithRequest:request delegate:self];
     601             :         if (download)
     602             :         {
     603             :                 _downloadProgress = 0;
     604             :                 _downloadExpected = 0;
     605             :                 NSString *label = DESC(@"oolite-oxzmanager-download-label-list");
     606             :                 if (_interfaceState != OXZ_STATE_UPDATING)
     607             :                 {
     608             :                         NSDictionary *expectedManifest = nil;
     609             :                         expectedManifest = [_filteredList objectAtIndex:_item];
     610             : 
     611             :                         label = [expectedManifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-download-label-oxz")];
     612             :                 }
     613             : 
     614             :                 [self setCurrentDownload:download withLabel:label]; // retains it
     615             :                 [download release];
     616             :                 OOLog(kOOOXZDebugLog,@"Download request received, using %@ and downloading to %@",[request URL],[self downloadPath]);
     617             :                 return YES;
     618             :         }
     619             :         else
     620             :         {
     621             :                 OOLog(kOOOXZErrorLog,@"Unable to start downloading file at %@",[request URL]);
     622             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     623             :                 return NO;
     624             :         }
     625             : }
     626             : 
     627             : 
     628             : - (BOOL) cancelUpdate
     629             : {
     630             :         if (!(_interfaceState == OXZ_STATE_UPDATING || _interfaceState == OXZ_STATE_INSTALLING) || _downloadStatus == OXZ_DOWNLOAD_NONE)
     631             :         {
     632             :                 return NO;
     633             :         }
     634             :         OOLog(kOOOXZDebugLog, @"%@", @"Trying to cancel file download");
     635             :         if (_currentDownload != nil)
     636             :         {
     637             :                 [_currentDownload cancel];
     638             :         }
     639             :         else if (_downloadStatus == OXZ_DOWNLOAD_COMPLETE)
     640             :         {
     641             :                 NSString *path = [self downloadPath];
     642             :                 [[NSFileManager defaultManager] oo_removeItemAtPath:path];
     643             :         }
     644             :         _downloadStatus = OXZ_DOWNLOAD_NONE;
     645             :         if (_interfaceState == OXZ_STATE_INSTALLING)
     646             :         {
     647             :                 _interfaceState = OXZ_STATE_PICK_INSTALL;
     648             :         }
     649             :         else
     650             :         {
     651             :                 _interfaceState = OXZ_STATE_MAIN;
     652             :         }
     653             :         [self gui];
     654             :         return YES;
     655             : }
     656             : 
     657             : 
     658             : - (NSArray *) manifests
     659             : {
     660             :         return _oxzList;
     661             : }
     662             : 
     663             : 
     664             : - (NSArray *) managedOXZs
     665             : {
     666             :         if (_managedList == nil)
     667             :         {
     668             :                 // if this list is being reset, also reset the current install list
     669             :                 [ResourceManager resetManifestKnowledgeForOXZManager];
     670             :                 NSArray *managedOXZs = [[NSFileManager defaultManager] oo_directoryContentsAtPath:[self installPath]];
     671             :                 NSMutableArray *manifests = [NSMutableArray arrayWithCapacity:[managedOXZs count]];
     672             :                 NSString *filename = nil;
     673             :                 NSString *fullpath = nil;
     674             :                 NSDictionary *manifest = nil;
     675             :                 foreach (filename, managedOXZs)
     676             :                 {
     677             :                         fullpath = [[self installPath] stringByAppendingPathComponent:filename];
     678             :                         manifest = OODictionaryFromFile([fullpath stringByAppendingPathComponent:@"manifest.plist"]);
     679             :                         if (manifest != nil)
     680             :                         {
     681             :                                 NSMutableDictionary *adjManifest = [NSMutableDictionary dictionaryWithDictionary:manifest];
     682             :                                 [adjManifest setObject:fullpath forKey:kOOManifestFilePath];
     683             : 
     684             :                                 NSDictionary *stored = nil;
     685             :                                 /* The list is already sorted to put the latest
     686             :                                  * versions first. This flag means that it stops
     687             :                                  * checking the list for versions once it finds one
     688             :                                  * that is plausibly installable */
     689             :                                 BOOL foundInstallable = NO;
     690             :                                 foreach (stored, _oxzList)
     691             :                                 {
     692             :                                         if ([[stored oo_stringForKey:kOOManifestIdentifier] isEqualToString:[manifest oo_stringForKey:kOOManifestIdentifier]])
     693             :                                         {
     694             :                                                 if (foundInstallable == NO)
     695             :                                                 {
     696             :                                                         [adjManifest setObject:[stored oo_stringForKey:kOOManifestVersion] forKey:kOOManifestAvailableVersion];
     697             :                                                         [adjManifest setObject:[stored oo_stringForKey:kOOManifestDownloadURL] forKey:kOOManifestDownloadURL];
     698             :                                                         if ([ResourceManager checkVersionCompatibility:manifest forOXP:nil])
     699             :                                                         {
     700             :                                                                 foundInstallable = YES;
     701             :                                                         }
     702             :                                                 }
     703             :                                         }
     704             :                                 }
     705             : 
     706             :                                 [manifests addObject:adjManifest];
     707             :                         }
     708             :                 }
     709             :                 [manifests sortUsingFunction:oxzSort context:NULL];
     710             : 
     711             :                 _managedList = [manifests copy];
     712             :         }
     713             :         return _managedList;
     714             : }
     715             : 
     716             : 
     717           0 : - (BOOL) processDownloadedManifests
     718             : {
     719             :         if (_downloadStatus != OXZ_DOWNLOAD_COMPLETE)
     720             :         {
     721             :                 return NO;
     722             :         }
     723             :         [self setOXZList:OOArrayFromFile([self downloadPath])];
     724             :         if (_oxzList != nil)
     725             :         {
     726             :                 [_oxzList writeToFile:[self manifestPath] atomically:YES];
     727             :                 // and clean up the temp file
     728             :                 [[NSFileManager defaultManager] oo_removeItemAtPath:[self downloadPath]];
     729             :                 // invalidate the managed list
     730             :                 DESTROY(_managedList);
     731             :                 _interfaceState = OXZ_STATE_TASKDONE;
     732             :                 [self gui];
     733             :                 return YES;
     734             :         }
     735             :         else
     736             :         {
     737             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     738             :                 OOLog(kOOOXZErrorLog,@"Downloaded manifest was not a valid plist, has been left in %@",[self downloadPath]);
     739             :                 // revert to the old one
     740             :                 [self setOXZList:OOArrayFromFile([self manifestPath])];
     741             :                 _interfaceState = OXZ_STATE_TASKDONE;
     742             :                 [self gui];
     743             :                 return NO;
     744             :         }
     745             : }
     746             : 
     747             : 
     748           1 : - (BOOL) processDownloadedOXZ
     749             : {
     750             :         if (_downloadStatus != OXZ_DOWNLOAD_COMPLETE)
     751             :         {
     752             :                 return NO;
     753             :         }
     754             : 
     755             :         NSDictionary *downloadedManifest = OODictionaryFromFile([[self downloadPath] stringByAppendingPathComponent:@"manifest.plist"]);
     756             :         if (downloadedManifest == nil)
     757             :         {
     758             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     759             :                 OOLog(kOOOXZErrorLog,@"Downloaded OXZ does not contain a manifest.plist, has been left in %@",[self downloadPath]);
     760             :                 _interfaceState = OXZ_STATE_TASKDONE;
     761             :                 [self gui];
     762             :                 return NO;
     763             :         }
     764             :         NSDictionary *expectedManifest = nil;
     765             :         expectedManifest = [_filteredList objectAtIndex:_item];
     766             : 
     767             :         if (expectedManifest == nil || 
     768             :                 (![[downloadedManifest oo_stringForKey:kOOManifestIdentifier] isEqualToString:[expectedManifest oo_stringForKey:kOOManifestIdentifier]]) || 
     769             :                 (![[downloadedManifest oo_stringForKey:kOOManifestVersion] isEqualToString:[expectedManifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[expectedManifest oo_stringForKey:kOOManifestVersion]]])
     770             :                 )
     771             :         {
     772             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     773             :                 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.");
     774             :                 _interfaceState = OXZ_STATE_TASKDONE;
     775             :                 [self gui];
     776             :                 return NO;
     777             :         }
     778             :         // this appears to be the OXZ we expected
     779             :         // filename is going to be identifier.oxz
     780             :         NSString *filename = [[downloadedManifest oo_stringForKey:kOOManifestIdentifier] stringByAppendingString:@".oxz"];
     781             : 
     782             :         if (![self ensureInstallPath])
     783             :         {
     784             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     785             :                 OOLog(kOOOXZErrorLog, @"%@", @"Unable to create installation folder.");
     786             :                 _interfaceState = OXZ_STATE_TASKDONE;
     787             :                 [self gui];
     788             :                 return NO;
     789             :         }
     790             : 
     791             :         // delete filename if it exists from OXZ folder
     792             :         NSString *destination = [[self installPath] stringByAppendingPathComponent:filename];
     793             :         [[NSFileManager defaultManager] oo_removeItemAtPath:destination];
     794             : 
     795             :         // move the temp file on to it
     796             :         if (![[NSFileManager defaultManager] oo_moveItemAtPath:[self downloadPath] toPath:destination])
     797             :         {
     798             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
     799             :                 OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
     800             :                 _interfaceState = OXZ_STATE_TASKDONE;
     801             :                 [self gui];
     802             :                 return NO;
     803             :         }
     804             :         _changesMade = YES;
     805             :         DESTROY(_managedList); // will need updating
     806             :         // do this now to cope with circular dependencies on download
     807             :         [ResourceManager resetManifestKnowledgeForOXZManager];
     808             : 
     809             :         /** 
     810             :          * If downloadedManifest is in _dependencyStack, remove it
     811             :          * Get downloadedManifest requires_oxp list
     812             :          * Add entries ones to _dependencyStack
     813             :          * If _dependencyStack has contents, update _progressStatus
     814             :          * ...and start the download of the 'first' item in _dependencyStack
     815             :          * ...which isn't already installed (_dependencyStack is unordered
     816             :          * ...so 'first' isn't really defined)
     817             :          *
     818             :          * ...if the item in _dependencyStack is not findable (e.g. wrong
     819             :          * ...version) then stop here.
     820             :          */
     821             :         NSArray *requires = [downloadedManifest oo_arrayForKey:kOOManifestRequiresOXPs defaultValue:nil];
     822             :         if (requires == nil)
     823             :         {
     824             :                 // just in case the requirements are only specified in the online copy
     825             :                 requires = [expectedManifest oo_arrayForKey:kOOManifestRequiresOXPs defaultValue:nil];
     826             :         }
     827             :         NSDictionary *requirement = nil;
     828             :         NSMutableString *progress = [NSMutableString stringWithCapacity:2048];
     829             :         OOLog(kOOOXZDebugLog,@"Dependency stack has %llu elements",[_dependencyStack count]);
     830             : 
     831             :         if ([_dependencyStack count] > 0)
     832             :         {
     833             :                 // will remove as iterate, so create a temp copy to iterate over
     834             :                 NSSet *tempStack = [NSSet setWithSet:_dependencyStack];
     835             :                 foreach (requirement, tempStack)
     836             :                 {
     837             :                         OOLog(kOOOXZDebugLog,@"Dependency stack: checking %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     838             :                         if (![ResourceManager manifest:downloadedManifest HasUnmetDependency:requirement logErrors:NO]
     839             :                             && requires != nil && [requires containsObject:requirement])
     840             :                         {
     841             :                                 // it was unmet, but now it's met                                       
     842             :                                 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-now-has-@"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     843             :                                 [_dependencyStack removeObject:requirement];
     844             :                                 OOLog(kOOOXZDebugLog, @"%@", @"Dependency stack: requirement met");
     845             :                         } else if ([[requirement oo_stringForKey:kOOManifestRelationIdentifier] isEqualToString:[downloadedManifest oo_stringForKey:kOOManifestIdentifier]]) {
     846             :                                 // remove the requirement for the just downloaded OXP
     847             :                                 [_dependencyStack removeObject:requirement];
     848             :                         }
     849             :                 }
     850             :         }
     851             :         if (requires != nil)
     852             :         {
     853             :                 foreach (requirement, requires)
     854             :                 {
     855             :                         if ([ResourceManager manifest:downloadedManifest HasUnmetDependency:requirement logErrors:NO])
     856             :                         {
     857             :                                 OOLog(kOOOXZDebugLog,@"Dependency stack: adding %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     858             :                                 [_dependencyStack addObject:requirement];
     859             :                                 [progress appendFormat:DESC(@"oolite-oxzmanager-progress-requires-@"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     860             :                         }
     861             :                 }
     862             :         }
     863             :         if ([_dependencyStack count] > 0)
     864             :         {
     865             :                 // get an object from the requirements list, and download it
     866             :                 // if it can be found
     867             :                 BOOL undownloadedRequirement = NO;
     868             :                 NSDictionary *availableDownload = nil;
     869             :                 BOOL foundDownload = NO;
     870             :                 NSUInteger index = 0;
     871             :                 NSString *needsIdentifier = nil;
     872             : 
     873             :                 do
     874             :                 {
     875             :                         undownloadedRequirement = YES;
     876             :                         requirement = [_dependencyStack anyObject];
     877             :                         OOLog(kOOOXZDebugLog,@"Dependency stack: next is %@",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     878             : 
     879             :                         if (!_downloadAllDependencies)
     880             :                         {
     881             :                                 [progress appendString:DESC(@"oolite-oxzmanager-progress-get-required")];
     882             :                         }
     883             :                         needsIdentifier = [requirement oo_stringForKey:kOOManifestRelationIdentifier];
     884             :                 
     885             :                         foreach (availableDownload, _oxzList)
     886             :                         {
     887             :                                 if ([[availableDownload oo_stringForKey:kOOManifestIdentifier] isEqualToString:needsIdentifier])
     888             :                                 {
     889             :                                         if ([ResourceManager matchVersions:requirement withVersion:[availableDownload oo_stringForKey:kOOManifestVersion]])
     890             :                                         {
     891             :                                                 OOLog(kOOOXZDebugLog, @"%@", @"Dependency stack: found download for next item");
     892             :                                                 foundDownload = YES;
     893             :                                                 index = [_oxzList indexOfObject:availableDownload];
     894             :                                                 break;
     895             :                                         }
     896             :                                 }
     897             :                         }
     898             :                         
     899             :                         if (foundDownload)
     900             :                         {
     901             :                                 if ([self installableState:[_oxzList objectAtIndex:index]] == OXZ_UNINSTALLABLE_ALREADY)
     902             :                                 {
     903             :                                         OOLog(kOOOXZDebugLog,@"Dependency stack: %@ is downloaded but not yet loadable, removing from list.",[requirement oo_stringForKey:kOOManifestRelationIdentifier]);
     904             :                                         // then this has already been downloaded, but
     905             :                                         // can't be configured yet presumably because
     906             :                                         // another dependency is still to be loaded
     907             :                                         [_dependencyStack removeObject:requirement];
     908             :                                         if ([_dependencyStack count] > 0)
     909             :                                         {
     910             :                                                 // try again
     911             :                                                 undownloadedRequirement = NO;
     912             :                                         }
     913             :                                         else
     914             :                                         {
     915             :                                                 // this case should probably never happen
     916             :                                                 // is handled below just in case
     917             :                                                 foundDownload = NO;
     918             :                                         }
     919             :                                 }
     920             :                         }
     921             :                 }
     922             :                 while (!undownloadedRequirement);
     923             : 
     924             :                 if (foundDownload)
     925             :                 {
     926             :                         // must clear filters entirely at this point
     927             :                         [self setFilteredList:_oxzList];
     928             :                         // then download that item
     929             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
     930             :                         if (_downloadAllDependencies)
     931             :                         {
     932             :                                 OOLog(kOOOXZDebugLog,@"Dependency stack: installing %llu from list",index);
     933             :                                 if (![self installOXZ:index]) {
     934             :                                         // if a required dependency is somehow uninstallable
     935             :                                         // e.g. required+maximum version don't match this Oolite
     936             :                                         [progress appendFormat:DESC(@"oolite-oxzmanager-progress-required-@-not-found"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     937             :                                         [self setProgressStatus:progress];
     938             :                                         OOLog(kOOOXZErrorLog,@"OXZ dependency %@ could not be found for automatic download.",needsIdentifier);
     939             :                                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
     940             :                                         OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
     941             :                                         _interfaceState = OXZ_STATE_TASKDONE;
     942             :                                         [self gui];
     943             :                                         return NO;
     944             :                                 }
     945             :                         }
     946             :                         else
     947             :                         {
     948             :                                 _interfaceState = OXZ_STATE_DEPENDENCIES;
     949             :                                 _item = index;
     950             :                         }
     951             :                         [self setProgressStatus:progress];
     952             :                         [self gui];
     953             :                         return YES;
     954             :                 }
     955             :                 // this is probably always the case, see above
     956             :                 else if ([_dependencyStack count] > 0)
     957             :                 {
     958             :                         [progress appendFormat:DESC(@"oolite-oxzmanager-progress-required-@-not-found"),[requirement oo_stringForKey:kOOManifestRelationDescription defaultValue:[requirement oo_stringForKey:kOOManifestRelationIdentifier]]];
     959             :                         [self setProgressStatus:progress];
     960             :                         OOLog(kOOOXZErrorLog,@"OXZ dependency %@ could not be found for automatic download.",needsIdentifier);
     961             :                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
     962             :                         OOLog(kOOOXZErrorLog, @"%@", @"Downloaded OXZ could not be installed.");
     963             :                         _interfaceState = OXZ_STATE_TASKDONE;
     964             :                         [self gui];
     965             :                         return NO;
     966             :                 }
     967             :         }
     968             : 
     969             :         [self setProgressStatus:@""];
     970             :         _interfaceState = OXZ_STATE_TASKDONE;
     971             :         [_dependencyStack removeAllObjects]; // just in case
     972             :         _downloadAllDependencies = NO;
     973             :         [self gui];
     974             :         return YES;
     975             : }
     976             : 
     977             : 
     978           0 : - (NSDictionary *) installedManifestForIdentifier:(NSString *)identifier
     979             : {
     980             :         NSArray *installed = [self managedOXZs];
     981             :         NSDictionary *manifest = nil;
     982             :         foreach (manifest,installed)
     983             :         {
     984             :                 if ([[manifest oo_stringForKey:kOOManifestIdentifier] isEqualToString:identifier])
     985             :                 {
     986             :                         return manifest;
     987             :                 }
     988             :         }
     989             :         return nil;
     990             : }
     991             : 
     992             : 
     993           0 : - (OXZInstallableState) installableState:(NSDictionary *)manifest
     994             : {
     995             :         NSString *title = [manifest oo_stringForKey:kOOManifestTitle defaultValue:nil];
     996             :         NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier defaultValue:nil];
     997             :         /* Check Oolite version */
     998             :         if (![ResourceManager checkVersionCompatibility:manifest forOXP:title])
     999             :         {
    1000             :                 return OXZ_UNINSTALLABLE_VERSION;
    1001             :         }
    1002             :         /* Check for current automated install */
    1003             :         NSDictionary *installed = [self installedManifestForIdentifier:identifier];
    1004             :         if (installed == nil)
    1005             :         {
    1006             :                 // check for manual install
    1007             :                 installed = [ResourceManager manifestForIdentifier:identifier];
    1008             :         }
    1009             : 
    1010             :         if (installed != nil)
    1011             :         {
    1012             :                 if (![[installed oo_stringForKey:kOOManifestFilePath] hasPrefix:[self installPath]])
    1013             :                 {
    1014             :                         // installed manually
    1015             :                         return OXZ_UNINSTALLABLE_MANUAL;
    1016             :                 }
    1017             :                 if ([[installed oo_stringForKey:kOOManifestVersion] isEqualToString:[manifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[manifest oo_stringForKey:kOOManifestVersion]]]
    1018             :                         && [[NSFileManager defaultManager] fileExistsAtPath:[installed oo_stringForKey:kOOManifestFilePath]])
    1019             :                 {
    1020             :                         // installed this exact version already, and haven't
    1021             :                         // uninstalled it since entering the manager, and it's
    1022             :                         // still available
    1023             :                         return OXZ_UNINSTALLABLE_ALREADY;
    1024             :                 }
    1025             :                 else if ([installed oo_stringForKey:kOOManifestAvailableVersion defaultValue:nil] == nil)
    1026             :                 {
    1027             :                         // installed, but no remote copy is indexed any more
    1028             :                         return OXZ_UNINSTALLABLE_NOREMOTE;
    1029             :                 }
    1030             :         }
    1031             :         /* Check for dependencies being met */
    1032             :         if ([ResourceManager manifestHasConflicts:manifest logErrors:NO])
    1033             :         {
    1034             :                 return OXZ_INSTALLABLE_CONFLICTS;
    1035             :         }
    1036             :         if (installed != nil)
    1037             :         {
    1038             :                 NSString *availableVersion = [manifest oo_stringForKey:kOOManifestAvailableVersion];
    1039             :                 if (availableVersion == nil)
    1040             :                 {
    1041             :                         availableVersion = [manifest oo_stringForKey:kOOManifestVersion];
    1042             :                 }
    1043             :                 NSString *installedVersion = [installed oo_stringForKey:kOOManifestVersion];
    1044             :                 OOLog(@"version.debug",@"%@ mv:%@ mav:%@",identifier,installedVersion,availableVersion);
    1045             :                 if (CompareVersions(ComponentsFromVersionString(installedVersion),ComponentsFromVersionString(availableVersion)) == NSOrderedDescending)
    1046             :                 {
    1047             :                         // the installed copy is more recent than the server copy
    1048             :                         return OXZ_UNINSTALLABLE_NOREMOTE;
    1049             :                 }
    1050             :                 return OXZ_INSTALLABLE_UPDATE;
    1051             :         }
    1052             :         if ([ResourceManager manifestHasMissingDependencies:manifest logErrors:NO])
    1053             :         {
    1054             :                 return OXZ_INSTALLABLE_DEPENDENCIES;
    1055             :         }
    1056             :         return OXZ_INSTALLABLE_OKAY;
    1057             : }
    1058             : 
    1059             : 
    1060           0 : - (OOColor *) colorForManifest:(NSDictionary *)manifest 
    1061             : {
    1062             :         switch ([self installableState:manifest])
    1063             :         {
    1064             :         case OXZ_INSTALLABLE_OKAY:
    1065             :                 return [OOColor yellowColor];
    1066             :         case OXZ_INSTALLABLE_UPDATE:
    1067             :                 return [OOColor cyanColor];
    1068             :         case OXZ_INSTALLABLE_DEPENDENCIES:
    1069             :                 return [OOColor orangeColor];
    1070             :         case OXZ_INSTALLABLE_CONFLICTS:
    1071             :                 return [OOColor brownColor];
    1072             :         case OXZ_UNINSTALLABLE_ALREADY:
    1073             :                 return [OOColor whiteColor];
    1074             :         case OXZ_UNINSTALLABLE_MANUAL:
    1075             :                 return [OOColor redColor];
    1076             :         case OXZ_UNINSTALLABLE_VERSION:
    1077             :                 return [OOColor grayColor];
    1078             :         case OXZ_UNINSTALLABLE_NOREMOTE:
    1079             :                 return [OOColor blueColor];
    1080             :         }
    1081             :         return [OOColor yellowColor]; // never
    1082             : }
    1083             : 
    1084             : 
    1085           0 : - (NSString *) installStatusForManifest:(NSDictionary *)manifest 
    1086             : {
    1087             :         switch ([self installableState:manifest])
    1088             :         {
    1089             :         case OXZ_INSTALLABLE_OKAY:
    1090             :                 return DESC(@"oolite-oxzmanager-installable-okay");
    1091             :         case OXZ_INSTALLABLE_UPDATE:
    1092             :                 return DESC(@"oolite-oxzmanager-installable-update");
    1093             :         case OXZ_INSTALLABLE_DEPENDENCIES:
    1094             :                 return DESC(@"oolite-oxzmanager-installable-depend");
    1095             :         case OXZ_INSTALLABLE_CONFLICTS:
    1096             :                 return DESC(@"oolite-oxzmanager-installable-conflicts");
    1097             :         case OXZ_UNINSTALLABLE_ALREADY:
    1098             :                 return DESC(@"oolite-oxzmanager-installable-already");
    1099             :         case OXZ_UNINSTALLABLE_MANUAL:
    1100             :                 return DESC(@"oolite-oxzmanager-installable-manual");
    1101             :         case OXZ_UNINSTALLABLE_VERSION:
    1102             :                 return DESC(@"oolite-oxzmanager-installable-version");
    1103             :         case OXZ_UNINSTALLABLE_NOREMOTE:
    1104             :                 return DESC(@"oolite-oxzmanager-installable-noremote");
    1105             :         }
    1106             :         return nil; // never
    1107             : }
    1108             : 
    1109             : 
    1110             : 
    1111             : - (void) gui
    1112             : {
    1113             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1114             :         OOGUIRow                startRow = OXZ_GUI_ROW_EXIT;
    1115             : 
    1116             : #if OOLITE_WINDOWS
    1117             :         /* unlock OXZs ahead of potential changes by making sure sound
    1118             :          * files aren't being held open */
    1119             :         [ResourceManager clearCaches];
    1120             :         [PLAYER destroySound];
    1121             : #endif
    1122             : 
    1123             :         [gui clearAndKeepBackground:YES];
    1124             :         [gui setTitle:DESC(@"oolite-oxzmanager-title")];
    1125             : 
    1126             :         /* This switch will give warnings unless all states are
    1127             :          * covered. */
    1128             :         switch (_interfaceState)
    1129             :         {
    1130             :         case OXZ_STATE_SETFILTER:
    1131             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-setfilter")];
    1132             :                 [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-currentfilter-is-@"),_currentFilter] forRow:OXZ_GUI_ROW_FILTERCURRENT align:GUI_ALIGN_LEFT];
    1133             :                 [gui addLongText:DESC(@"oolite-oxzmanager-filterhelp") startingAtRow:OXZ_GUI_ROW_FILTERHELP align:GUI_ALIGN_LEFT];
    1134             : 
    1135             :                 
    1136             :                 return; // don't do normal row selection stuff
    1137             :         case OXZ_STATE_NODATA:
    1138             :                 if (_oxzList == nil)
    1139             :                 {
    1140             :                         [gui addLongText:DESC(@"oolite-oxzmanager-firstrun") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1141             :                         [gui setText:DESC(@"oolite-oxzmanager-download-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1142             :                         [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
    1143             : 
    1144             :                         startRow = OXZ_GUI_ROW_UPDATE;
    1145             :                 }
    1146             :                 else
    1147             :                 {
    1148             :                         // update data  
    1149             :                         [gui addLongText:DESC(@"oolite-oxzmanager-secondrun") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1150             :                         [gui setText:DESC(@"oolite-oxzmanager-download-noupdate") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1151             :                         [gui setKey:@"_MAIN" forRow:OXZ_GUI_ROW_PROCEED];
    1152             : 
    1153             :                         [gui setText:DESC(@"oolite-oxzmanager-update-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1154             :                         [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
    1155             : 
    1156             :                         startRow = OXZ_GUI_ROW_PROCEED;
    1157             :                 }
    1158             :                 break;
    1159             :         case OXZ_STATE_RESTARTING:
    1160             :                 [gui addLongText:DESC(@"oolite-oxzmanager-restart") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1161             :                 return; // yes, return, not break: controls are pointless here
    1162             :         case OXZ_STATE_MAIN:
    1163             :                 [gui addLongText:DESC(@"oolite-oxzmanager-intro") startingAtRow:OXZ_GUI_ROW_FIRSTRUN align:GUI_ALIGN_LEFT];
    1164             :                 // fall through
    1165             :         case OXZ_STATE_PICK_INSTALL:
    1166             :         case OXZ_STATE_PICK_INSTALLED:
    1167             :         case OXZ_STATE_PICK_REMOVE:
    1168             :                 if (_interfaceState != OXZ_STATE_MAIN)
    1169             :                 {
    1170             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-currentfilter-is-@-@"),OOExpand(@"[oolite_key_oxzmanager_setfilter]"),_currentFilter] forRow:OXZ_GUI_ROW_LISTFILTER align:GUI_ALIGN_LEFT];
    1171             :                         [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTFILTER];
    1172             :                 }
    1173             : 
    1174             :                 [gui setText:DESC(@"oolite-oxzmanager-install") forRow:OXZ_GUI_ROW_INSTALL align:GUI_ALIGN_CENTER];
    1175             :                 [gui setKey:@"_INSTALL" forRow:OXZ_GUI_ROW_INSTALL];
    1176             :                 [gui setText:DESC(@"oolite-oxzmanager-installed") forRow:OXZ_GUI_ROW_INSTALLED align:GUI_ALIGN_CENTER];
    1177             :                 [gui setKey:@"_INSTALLED" forRow:OXZ_GUI_ROW_INSTALLED];
    1178             :                 [gui setText:DESC(@"oolite-oxzmanager-remove") forRow:OXZ_GUI_ROW_REMOVE align:GUI_ALIGN_CENTER];
    1179             :                 [gui setKey:@"_REMOVE" forRow:OXZ_GUI_ROW_REMOVE];
    1180             :                 [gui setText:DESC(@"oolite-oxzmanager-update-list") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1181             :                 [gui setKey:@"_UPDATE" forRow:OXZ_GUI_ROW_UPDATE];
    1182             :                 [gui setText:DESC(@"oolite-oxzmanager-update-all") forRow:OXZ_GUI_ROW_UPDATE_ALL align:GUI_ALIGN_CENTER];
    1183             :                 [gui setKey:@"_UPDATE_ALL" forRow:OXZ_GUI_ROW_UPDATE_ALL];
    1184             : 
    1185             :                 startRow = OXZ_GUI_ROW_INSTALL;
    1186             :                 break;
    1187             :         case OXZ_STATE_UPDATING:
    1188             :         case OXZ_STATE_INSTALLING:
    1189             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-downloading")];
    1190             : 
    1191             :                 [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];
    1192             : 
    1193             :                 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+2 align:GUI_ALIGN_LEFT];
    1194             : 
    1195             :                 [gui setText:DESC(@"oolite-oxzmanager-cancel") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
    1196             :                 [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
    1197             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1198             :                 break;
    1199             :         case OXZ_STATE_DEPENDENCIES:
    1200             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-dependencies")];
    1201             : 
    1202             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-decision") forRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1203             : 
    1204             :                 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+2 align:GUI_ALIGN_LEFT];
    1205             : 
    1206             :                 startRow = OXZ_GUI_ROW_INSTALLED;
    1207             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-yes-all") forRow:OXZ_GUI_ROW_INSTALLED align:GUI_ALIGN_CENTER];
    1208             :                 [gui setKey:@"_PROCEED_ALL" forRow:OXZ_GUI_ROW_INSTALLED];
    1209             : 
    1210             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-yes") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1211             :                 [gui setKey:@"_PROCEED" forRow:OXZ_GUI_ROW_PROCEED];
    1212             : 
    1213             :                 [gui setText:DESC(@"oolite-oxzmanager-dependencies-no") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
    1214             :                 [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
    1215             :                 break;
    1216             : 
    1217             :         case OXZ_STATE_REMOVING:
    1218             :                 [gui addLongText:DESC(@"oolite-oxzmanager-removal-done") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1219             :                 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1220             :                 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
    1221             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1222             :                 break;
    1223             :         case OXZ_STATE_TASKDONE:
    1224             :                 if (_downloadStatus == OXZ_DOWNLOAD_COMPLETE)
    1225             :                 {
    1226             :                         [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];
    1227             :                 }
    1228             :                 else
    1229             :                 {
    1230             :                         [gui addLongText:OOExpandKey(@"oolite-oxzmanager-progress-error") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1231             :                 }
    1232             :                 [gui addLongText:_progressStatus startingAtRow:OXZ_GUI_ROW_PROGRESS+4 align:GUI_ALIGN_LEFT];
    1233             : 
    1234             :                 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1235             :                 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
    1236             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1237             :                 break;
    1238             :         case OXZ_STATE_EXTRACT:
    1239             :                 {
    1240             :                         NSDictionary *manifest = [_filteredList oo_dictionaryAtIndex:_item];
    1241             :                         NSString *title = [manifest oo_stringForKey:kOOManifestTitle];
    1242             :                         NSString *version = [manifest oo_stringForKey:kOOManifestVersion];
    1243             :                         NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier];
    1244             :                         [gui setTitle:DESC(@"oolite-oxzmanager-title-extract")];
    1245             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-title-@-version-@"),
    1246             :                                                                    title,
    1247             :                                                                    version]
    1248             :                                   forRow:0 align:GUI_ALIGN_LEFT];
    1249             :                         [gui addLongText:DESC(@"oolite-oxzmanager-extract-info") startingAtRow:2 align:GUI_ALIGN_LEFT];
    1250             : #ifdef NDEBUG
    1251             :                         [gui addLongText:DESC(@"oolite-oxzmanager-extract-releasebuild") startingAtRow:7 align:GUI_ALIGN_LEFT];
    1252             :                         [gui setColor:[OOColor orangeColor] forRow:7];
    1253             :                         [gui setColor:[OOColor orangeColor] forRow:8];
    1254             : #endif
    1255             :                         NSString *path = [self extractionBasePathForIdentifier:identifier andVersion:version];
    1256             :                         if ([[NSFileManager defaultManager] fileExistsAtPath:path])
    1257             :                         {
    1258             :                                 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-extract-@-already-exists"), path]
    1259             :                                   startingAtRow:10 align:GUI_ALIGN_LEFT];
    1260             :                                 startRow = OXZ_GUI_ROW_CANCEL;
    1261             :                                 [gui setText:DESC(@"oolite-oxzmanager-extract-unavailable") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1262             :                                 [gui setColor:[OOColor grayColor] forRow:OXZ_GUI_ROW_PROCEED];
    1263             :                         }
    1264             :                         else
    1265             :                         {
    1266             :                                 [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-extract-to-@"), path]
    1267             :                                   startingAtRow:10 align:GUI_ALIGN_LEFT];
    1268             :                                 startRow = OXZ_GUI_ROW_PROCEED;
    1269             :                                 [gui setText:DESC(@"oolite-oxzmanager-extract-proceed") forRow:OXZ_GUI_ROW_PROCEED align:GUI_ALIGN_CENTER];
    1270             :                                 [gui setKey:@"_PROCEED" forRow:OXZ_GUI_ROW_PROCEED];
    1271             : 
    1272             :                         }
    1273             :                         [gui setText:DESC(@"oolite-oxzmanager-extract-cancel") forRow:OXZ_GUI_ROW_CANCEL align:GUI_ALIGN_CENTER];
    1274             :                         [gui setKey:@"_CANCEL" forRow:OXZ_GUI_ROW_CANCEL];
    1275             : 
    1276             :                 }       
    1277             :                 break;
    1278             :         case OXZ_STATE_EXTRACTDONE:
    1279             :                 [gui addLongText:_progressStatus startingAtRow:1 align:GUI_ALIGN_LEFT];
    1280             :                 [gui setText:DESC(@"oolite-oxzmanager-acknowledge") forRow:OXZ_GUI_ROW_UPDATE align:GUI_ALIGN_CENTER];
    1281             :                 [gui setKey:@"_ACK" forRow:OXZ_GUI_ROW_UPDATE];
    1282             :                 startRow = OXZ_GUI_ROW_UPDATE;
    1283             :                 break;
    1284             : 
    1285             :         }
    1286             : 
    1287             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL)
    1288             :         {
    1289             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-install")];
    1290             :                 [self setFilteredList:[self applyCurrentFilter:_oxzList]];
    1291             :                 startRow = [self showInstallOptions];
    1292             :         }
    1293             :         else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    1294             :         {
    1295             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-installed")];
    1296             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    1297             :                 startRow = [self showInstallOptions];
    1298             :         }
    1299             :         else if (_interfaceState == OXZ_STATE_PICK_REMOVE)
    1300             :         {
    1301             :                 [gui setTitle:DESC(@"oolite-oxzmanager-title-remove")];
    1302             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    1303             :                 startRow = [self showRemoveOptions];
    1304             :         }
    1305             : 
    1306             : 
    1307             :         if (_changesMade)
    1308             :         {
    1309             :                 [gui setText:DESC(@"oolite-oxzmanager-exit-restart") forRow:OXZ_GUI_ROW_EXIT align:GUI_ALIGN_CENTER];
    1310             :         }
    1311             :         else
    1312             :         {
    1313             :                 [gui setText:DESC(@"oolite-oxzmanager-exit") forRow:OXZ_GUI_ROW_EXIT align:GUI_ALIGN_CENTER];
    1314             :         }
    1315             :         [gui setKey:@"_EXIT" forRow:OXZ_GUI_ROW_EXIT];
    1316             :         [gui setSelectableRange:NSMakeRange(startRow,2+(OXZ_GUI_ROW_EXIT-startRow))];
    1317             :         if (startRow < OXZ_GUI_ROW_INSTALL)
    1318             :         {
    1319             :                 [gui setSelectedRow:OXZ_GUI_ROW_INSTALL];
    1320             :         }
    1321             :         else if (_interfaceState == OXZ_STATE_NODATA)
    1322             :         {
    1323             :                 [gui setSelectedRow:OXZ_GUI_ROW_UPDATE];
    1324             :         }
    1325             :         else
    1326             :         {
    1327             :                 [gui setSelectedRow:startRow];
    1328             :         }
    1329             :         
    1330             : }
    1331             : 
    1332             : 
    1333             : - (BOOL) isRestarting
    1334             : {
    1335             :         // for the restart
    1336             :         if (EXPECT_NOT(_interfaceState == OXZ_STATE_RESTARTING))
    1337             :         {
    1338             :                 // Rebuilds OXP search
    1339             :                 [ResourceManager reset];
    1340             :                 [UNIVERSE reinitAndShowDemo:YES];
    1341             :                 _changesMade = NO;
    1342             :                 _interfaceState = OXZ_STATE_MAIN;
    1343             :                 _downloadStatus = OXZ_DOWNLOAD_NONE; // clear error state
    1344             :                 return YES;
    1345             :         }
    1346             :         else
    1347             :         {
    1348             :                 return NO;
    1349             :         }
    1350             : }
    1351             : 
    1352             : 
    1353             : - (void) processSelection
    1354             : {
    1355             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1356             :         OOGUIRow selection = [gui selectedRow];
    1357             : 
    1358             :         if (selection == OXZ_GUI_ROW_EXIT)
    1359             :         {
    1360             :                 [self cancelUpdate]; // doesn't hurt if no update in progress
    1361             :                 [_dependencyStack removeAllObjects]; // cleanup
    1362             :                 _downloadAllDependencies = NO;
    1363             :                 _downloadStatus = OXZ_DOWNLOAD_NONE; // clear error state
    1364             :                 if (_changesMade)
    1365             :                 {
    1366             :                         _interfaceState = OXZ_STATE_RESTARTING;
    1367             :                 }
    1368             :                 else
    1369             :                 {
    1370             :                         [PLAYER setGuiToIntroFirstGo:YES];
    1371             :                         if (_oxzList != nil)
    1372             :                         {
    1373             :                                 _interfaceState = OXZ_STATE_MAIN;
    1374             :                         }
    1375             :                         else
    1376             :                         {
    1377             :                                 _interfaceState = OXZ_STATE_NODATA;
    1378             :                         }
    1379             :                         return;
    1380             :                 }
    1381             :         }
    1382             :         else if (selection == OXZ_GUI_ROW_UPDATE) // also == _CANCEL
    1383             :         {
    1384             :                 if (_interfaceState == OXZ_STATE_REMOVING)
    1385             :                 {
    1386             :                         _interfaceState = OXZ_STATE_PICK_REMOVE;
    1387             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
    1388             :                 }
    1389             :                 else if (_interfaceState == OXZ_STATE_TASKDONE || _interfaceState == OXZ_STATE_DEPENDENCIES)
    1390             :                 {
    1391             :                         [_dependencyStack removeAllObjects];
    1392             :                         _downloadAllDependencies = NO;
    1393             :                         _interfaceState = OXZ_STATE_PICK_INSTALL;
    1394             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
    1395             :                 }
    1396             :                 else if (_interfaceState == OXZ_STATE_EXTRACTDONE)
    1397             :                 {
    1398             :                         [_dependencyStack removeAllObjects];
    1399             :                         _downloadAllDependencies = NO;
    1400             :                         _interfaceState = OXZ_STATE_PICK_INSTALLED;
    1401             :                         _downloadStatus = OXZ_DOWNLOAD_NONE;
    1402             :                 }
    1403             :                 else if (_interfaceState == OXZ_STATE_INSTALLING || _interfaceState == OXZ_STATE_UPDATING)
    1404             :                 {
    1405             :                         [self cancelUpdate]; // sets interface state and download status
    1406             :                 }
    1407             :                 else if (_interfaceState == OXZ_STATE_EXTRACT)
    1408             :                 {
    1409             :                         _interfaceState = OXZ_STATE_MAIN;
    1410             :                 }
    1411             :                 else
    1412             :                 {
    1413             :                         [self updateManifests];
    1414             :                 }
    1415             :         }
    1416             :         else if (selection == OXZ_GUI_ROW_INSTALL)
    1417             :         {
    1418             :                 _interfaceState = OXZ_STATE_PICK_INSTALL;
    1419             :         }
    1420             :         else if (selection == OXZ_GUI_ROW_INSTALLED)
    1421             :         {
    1422             :                 if (_interfaceState == OXZ_STATE_DEPENDENCIES) // also == _PROCEED_ALL
    1423             :                 {
    1424             :                         _downloadAllDependencies = YES;
    1425             :                         [self installOXZ:_item];
    1426             :                 }
    1427             :                 else 
    1428             :                 {
    1429             :                         _interfaceState = OXZ_STATE_PICK_INSTALLED;
    1430             :                 }
    1431             :         }
    1432             :         else if (selection == OXZ_GUI_ROW_REMOVE) // also == _PROCEED
    1433             :         {
    1434             :                 if (_interfaceState == OXZ_STATE_DEPENDENCIES)
    1435             :                 {
    1436             :                         [self installOXZ:_item];
    1437             :                 }
    1438             :                 else if (_interfaceState == OXZ_STATE_NODATA)
    1439             :                 {
    1440             :                         _interfaceState = OXZ_STATE_MAIN;
    1441             :                 }
    1442             :                 else if (_interfaceState == OXZ_STATE_EXTRACT)
    1443             :                 {
    1444             :                         [self setProgressStatus:[self extractOXZ:_item]];
    1445             :                         _interfaceState = OXZ_STATE_EXTRACTDONE;
    1446             :                 }
    1447             :                 else
    1448             :                 {
    1449             :                         _interfaceState = OXZ_STATE_PICK_REMOVE;
    1450             :                 }
    1451             :         }
    1452             :         else if (selection == OXZ_GUI_ROW_UPDATE_ALL)
    1453             :         {
    1454             :                 OOLog(kOOOXZDebugLog, @"%@", @"Trying to update all managed OXPs");
    1455             :                 [self updateAllOXZ];
    1456             :         }
    1457             :         else if (selection == OXZ_GUI_ROW_LISTPREV)
    1458             :         {
    1459             :                 [self processOptionsPrev];
    1460             :                 return;
    1461             :         }
    1462             :         else if (selection == OXZ_GUI_ROW_LISTNEXT)
    1463             :         {
    1464             :                 [self processOptionsNext];
    1465             :                 return;
    1466             :         }
    1467             :         else
    1468             :         {
    1469             :                 NSUInteger item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
    1470             :                 if (_interfaceState == OXZ_STATE_PICK_REMOVE)
    1471             :                 {
    1472             :                         [self removeOXZ:item];
    1473             :                 }
    1474             :                 else if (_interfaceState == OXZ_STATE_PICK_INSTALL)
    1475             :                 {
    1476             :                         OOLog(kOOOXZDebugLog, @"Trying to install index %lu", (unsigned long)item);
    1477             :                         [self installOXZ:item];
    1478             :                 }
    1479             :                 else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    1480             :                 {
    1481             :                         OOLog(kOOOXZDebugLog, @"Trying to install index %lu", (unsigned long)item);
    1482             :                         [self installOXZ:item];
    1483             :                 }
    1484             : 
    1485             :         }
    1486             : 
    1487             :         [self gui]; // update GUI
    1488             : }
    1489             : 
    1490             : 
    1491             : - (BOOL) isAcceptingTextInput
    1492             : {
    1493             :         return (_interfaceState == OXZ_STATE_SETFILTER);
    1494             : }
    1495             : 
    1496             : 
    1497             : - (BOOL) isAcceptingGUIInput
    1498             : {
    1499             :         return !_interfaceShowingOXZDetail;
    1500             : }
    1501             : 
    1502             : 
    1503             : - (void) processTextInput:(NSString *)input
    1504             : {
    1505             :         if ([self validateFilter:input])
    1506             :         {
    1507             :                 if ([input length] > 0)
    1508             :                 {
    1509             :                         [self setFilter:input];
    1510             :                 } // else keep previous filter
    1511             :                 _interfaceState = OXZ_STATE_PICK_INSTALL;
    1512             :                 [self gui];
    1513             :         }
    1514             :         // else nothing
    1515             : }
    1516             : 
    1517             : 
    1518             : - (void) refreshTextInput:(NSString *)input
    1519             : {
    1520             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1521             :         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-text-prompt-@"), input] forRow:OXZ_GUI_ROW_INPUT align:GUI_ALIGN_LEFT];
    1522             :         if ([self validateFilter:input])
    1523             :         {
    1524             :                 [gui setColor:[OOColor cyanColor] forRow:OXZ_GUI_ROW_INPUT];
    1525             :         }
    1526             :         else
    1527             :         {
    1528             :                 [gui setColor:[OOColor orangeColor] forRow:OXZ_GUI_ROW_INPUT];
    1529             :         }
    1530             : }
    1531             : 
    1532             : 
    1533             : - (void) processFilterKey
    1534             : {
    1535             :         if (_interfaceShowingOXZDetail)
    1536             :         {
    1537             :                 _interfaceShowingOXZDetail = NO;
    1538             :         }
    1539             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_MAIN)
    1540             :         {
    1541             :                 _interfaceState = OXZ_STATE_SETFILTER;
    1542             :                 [[UNIVERSE gameView] resetTypedString];
    1543             :                 [self gui];
    1544             :         }
    1545             :         // else this key does nothing
    1546             : }
    1547             : 
    1548             : 
    1549             : - (void) processShowInfoKey
    1550             : {
    1551             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE)
    1552             :         {
    1553             :                 GuiDisplayGen   *gui = [UNIVERSE gui];
    1554             : 
    1555             :                 if (_interfaceShowingOXZDetail)
    1556             :                 {
    1557             :                         _interfaceShowingOXZDetail = NO;
    1558             :                         [self gui]; // restore screen
    1559             :                         // reset list selection position
    1560             :                         [gui setSelectedRow:(_item - _offset + OXZ_GUI_ROW_LISTSTART)];
    1561             :                         // and do the GUI again with the correct positions
    1562             :                         [self showOptionsUpdate]; // restore screen
    1563             :                 }
    1564             :                 else
    1565             :                 {
    1566             :                         OOGUIRow selection = [gui selectedRow];
    1567             :                         
    1568             :                         if (selection < OXZ_GUI_ROW_LISTSTART || selection >= OXZ_GUI_ROW_LISTSTART + OXZ_GUI_NUM_LISTROWS)
    1569             :                         {
    1570             :                                 // not on an OXZ
    1571             :                                 return;
    1572             :                         }
    1573             : 
    1574             : 
    1575             :                         _item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
    1576             : 
    1577             :                         NSDictionary *manifest = [_filteredList oo_dictionaryAtIndex:_item];
    1578             :                         _interfaceShowingOXZDetail = YES;
    1579             : 
    1580             :                         [gui clearAndKeepBackground:YES];
    1581             :                         [gui setTitle:DESC(@"oolite-oxzmanager-title-infopage")];
    1582             : 
    1583             : // title, version                       
    1584             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-title-@-version-@"),
    1585             :                                                                    [manifest oo_stringForKey:kOOManifestTitle],
    1586             :                                                                    [manifest oo_stringForKey:kOOManifestVersion]]
    1587             :                                   forRow:0 align:GUI_ALIGN_LEFT];
    1588             : 
    1589             : // author
    1590             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-author-@"),
    1591             :                                                                    [manifest oo_stringForKey:kOOManifestAuthor]]
    1592             :                                   forRow:1 align:GUI_ALIGN_LEFT];
    1593             : 
    1594             : // license
    1595             :                         [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-license-@"),
    1596             :                                                                    [manifest oo_stringForKey:kOOManifestLicense]]
    1597             :                                   startingAtRow:2 align:GUI_ALIGN_LEFT];
    1598             : // tags
    1599             :                         
    1600             :                         [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-tags-@"),[[manifest oo_arrayForKey:kOOManifestTags] componentsJoinedByString: @", "]]
    1601             :                                   startingAtRow:4  align:GUI_ALIGN_LEFT];
    1602             : // description
    1603             :                         [gui addLongText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-description-@"),[manifest oo_stringForKey:kOOManifestDescription]]
    1604             :                                   startingAtRow:7  align:GUI_ALIGN_LEFT];
    1605             : 
    1606             : // infoURL              
    1607             :                         NSString *infoURLString = [manifest oo_stringForKey:kOOManifestInformationURL];
    1608             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-oxzmanager-infopage-infourl-@"),
    1609             :                                                                    infoURLString]
    1610             :                                   forRow:25 align:GUI_ALIGN_LEFT];
    1611             :                         // copy url info text to clipboard automatically once we are in the oxz info page
    1612             :                         [[UNIVERSE gameView] stringToClipboard:infoURLString];    
    1613             :                                   
    1614             : // instructions
    1615             :                         [gui setText:OOExpand(DESC(@"oolite-oxzmanager-infopage-return")) forRow:27 align:GUI_ALIGN_CENTER];
    1616             :                         [gui setColor:[OOColor greenColor] forRow:27];
    1617             : 
    1618             :                 }
    1619             :         }
    1620             : }
    1621             : 
    1622             : 
    1623             : - (void) processExtractKey
    1624             : {
    1625             :         // TODO: Extraction functionality - converts an installed OXZ to
    1626             :         // an OXP in the main AddOns folder if it's safe to do so.
    1627             :         if (!_interfaceShowingOXZDetail && (_interfaceState == OXZ_STATE_PICK_INSTALLED || _interfaceState == OXZ_STATE_PICK_REMOVE))
    1628             :         {
    1629             :                 GuiDisplayGen   *gui = [UNIVERSE gui];
    1630             :                 OOGUIRow selection = [gui selectedRow];
    1631             :                 
    1632             :                 if (selection < OXZ_GUI_ROW_LISTSTART || selection >= OXZ_GUI_ROW_LISTSTART + OXZ_GUI_NUM_LISTROWS)
    1633             :                 {
    1634             :                         // not on an OXZ
    1635             :                         return;
    1636             :                 }
    1637             :                 
    1638             :                 _item = _offset + selection - OXZ_GUI_ROW_LISTSTART;
    1639             :                 _interfaceState = OXZ_STATE_EXTRACT;
    1640             :                 [self gui];
    1641             :         }
    1642             : }
    1643             : 
    1644             : 
    1645           0 : - (BOOL) installOXZ:(NSUInteger)item 
    1646             : {
    1647             :         NSArray *picklist = _filteredList;
    1648             : 
    1649             :         if ([picklist count] <= item)
    1650             :         {
    1651             :                 return NO;
    1652             :         }
    1653             :         NSDictionary *manifest = [picklist objectAtIndex:item];
    1654             :         _item = item;
    1655             : 
    1656             :         if ([self installableState:manifest] >= OXZ_UNINSTALLABLE_ALREADY)
    1657             :         {
    1658             :                 OOLog(kOOOXZDebugLog,@"Cannot install %@",manifest);
    1659             :                 // can't be installed on this version of Oolite, or already is installed
    1660             :                 return NO;
    1661             :         }
    1662             :         NSString *url = [manifest objectForKey:kOOManifestDownloadURL];
    1663             :         if (url == nil)
    1664             :         {
    1665             :                 OOLog(kOOOXZErrorLog, @"%@", @"Manifest does not have a download URL - cannot install");
    1666             :                 return NO;
    1667             :         }
    1668             :         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    1669             :         [request setHTTPShouldHandleCookies:NO];
    1670             :         if (_downloadStatus != OXZ_DOWNLOAD_NONE)
    1671             :         {
    1672             :                 return NO;
    1673             :         }
    1674             :         _downloadStatus = OXZ_DOWNLOAD_STARTED;
    1675             :         _interfaceState = OXZ_STATE_INSTALLING;
    1676             :         
    1677             :         [self setProgressStatus:@""];
    1678             :         return [self beginDownload:request];
    1679             : }
    1680             : 
    1681             : 
    1682           0 : - (BOOL) updateAllOXZ
    1683             : {
    1684             :         [_dependencyStack removeAllObjects];
    1685             :         _downloadAllDependencies = YES;
    1686             :         [self setFilteredList:_oxzList];
    1687             :         NSDictionary *manifest = nil;
    1688             : 
    1689             :         foreach (manifest,_oxzList)
    1690             :         {
    1691             :                 if ([self installableState:manifest] == OXZ_INSTALLABLE_UPDATE)
    1692             :                 {
    1693             :                         OOLog(kOOOXZDebugLog, @"Queuing in for update: %@", manifest);
    1694             :                         [_dependencyStack addObject:manifest];
    1695             :                 }
    1696             :         }
    1697             :         NSDictionary *first = [_dependencyStack anyObject];
    1698             :         NSString* identifier = [first oo_stringForKey:kOOManifestRelationIdentifier];
    1699             :         NSUInteger item = NSUIntegerMax;
    1700             :         NSDictionary *availableDownload = nil;
    1701             :         foreach (availableDownload, _oxzList)
    1702             :         {
    1703             :                 if ([[availableDownload oo_stringForKey:kOOManifestIdentifier] isEqualToString:identifier])
    1704             :                 {
    1705             :                         item = [_oxzList indexOfObject:availableDownload];
    1706             :                         break;
    1707             :                 }
    1708             :         }
    1709             :         return [self installOXZ:item];
    1710             : }
    1711             : 
    1712             : 
    1713           0 : - (NSArray *) installOptions
    1714             : {
    1715             :         NSUInteger start = _offset;
    1716             :         if (start >= [_filteredList count])
    1717             :         {
    1718             :                 start = 0;
    1719             :                 _offset = 0;
    1720             :         }
    1721             :         NSUInteger end = start + OXZ_GUI_NUM_LISTROWS;
    1722             :         if (end > [_filteredList count])
    1723             :         {
    1724             :                 end = [_filteredList count];
    1725             :         }
    1726             :         return [_filteredList subarrayWithRange:NSMakeRange(start,end-start)];
    1727             : }
    1728             : 
    1729             : 
    1730             : - (OOGUIRow) showInstallOptions
    1731             : {
    1732             :         // shows the current installation options page
    1733             :         OOGUIRow startRow = OXZ_GUI_ROW_LISTPREV;
    1734             :         NSArray *options = [self installOptions];
    1735             :         NSUInteger optCount = [_filteredList count];
    1736             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1737             :         OOGUITabSettings tab_stops;
    1738             :         tab_stops[0] = 0;
    1739             :         tab_stops[1] = 100;
    1740             :         tab_stops[2] = 320;
    1741             :         tab_stops[3] = 400;
    1742             :         [gui setTabStops:tab_stops];
    1743             :         
    1744             : 
    1745             :         [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-heading-category"),
    1746             :                                                    DESC(@"oolite-oxzmanager-heading-title"), 
    1747             :                                                    DESC(@"oolite-oxzmanager-heading-installed"), 
    1748             :                                                    DESC(@"oolite-oxzmanager-heading-downloadable"), 
    1749             :                                                                 nil] forRow:OXZ_GUI_ROW_LISTHEAD];
    1750             : 
    1751             :         if (_offset > 0)
    1752             :         {
    1753             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTPREV];
    1754             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"",@"",@" <-- ", nil] forRow:OXZ_GUI_ROW_LISTPREV];
    1755             :                 [gui setKey:@"_BACK" forRow:OXZ_GUI_ROW_LISTPREV];
    1756             :         }
    1757             :         else
    1758             :         {
    1759             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
    1760             :                 {
    1761             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    1762             :                 }
    1763             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTPREV align:GUI_ALIGN_LEFT];
    1764             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTPREV];
    1765             :         }
    1766             :         if (_offset + 10 < optCount)
    1767             :         {
    1768             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTNEXT];
    1769             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"",@"",@" --> ", nil] forRow:OXZ_GUI_ROW_LISTNEXT];
    1770             :                 [gui setKey:@"_NEXT" forRow:OXZ_GUI_ROW_LISTNEXT];
    1771             :         }
    1772             :         else
    1773             :         {
    1774             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
    1775             :                 {
    1776             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    1777             :                 }
    1778             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTNEXT align:GUI_ALIGN_LEFT];
    1779             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTNEXT];
    1780             :         }
    1781             : 
    1782             :         // clear any previous longtext
    1783             :         for (NSUInteger i = OXZ_GUI_ROW_LISTSTATUS; i < OXZ_GUI_ROW_INSTALL-1; i++)
    1784             :         {
    1785             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    1786             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    1787             :         }
    1788             :         // and any previous listed entries
    1789             :         for (NSUInteger i = OXZ_GUI_ROW_LISTSTART; i < OXZ_GUI_ROW_LISTNEXT; i++)
    1790             :         {
    1791             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    1792             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    1793             :         }
    1794             : 
    1795             :         OOGUIRow row = OXZ_GUI_ROW_LISTSTART;
    1796             :         NSDictionary *manifest = nil;
    1797             :         BOOL oxzLineSelected = NO;
    1798             : 
    1799             :         foreach (manifest, options)
    1800             :         {
    1801             :                 NSDictionary *installed = [ResourceManager manifestForIdentifier:[manifest oo_stringForKey:kOOManifestIdentifier]];
    1802             :                 NSString *localPath = [[[self installPath] stringByAppendingPathComponent:[manifest oo_stringForKey:kOOManifestIdentifier]] stringByAppendingPathExtension:@"oxz"];
    1803             :                 if (installed == nil)
    1804             :                 {
    1805             :                         // check that there's not one just been downloaded
    1806             :                         installed = OODictionaryFromFile([localPath stringByAppendingPathComponent:@"manifest.plist"]);
    1807             :                 }
    1808             :                 else
    1809             :                 {
    1810             :                         // check for a more recent download
    1811             :                         if ([[NSFileManager defaultManager] fileExistsAtPath:localPath])
    1812             :                         {
    1813             :                                 
    1814             :                                 installed = OODictionaryFromFile([localPath stringByAppendingPathComponent:@"manifest.plist"]);
    1815             :                         }
    1816             :                         else
    1817             :                         {
    1818             :                                 // check if this was a managed OXZ which has been deleted
    1819             :                                 if ([[installed oo_stringForKey:kOOManifestFilePath] hasPrefix:[self installPath]])
    1820             :                                 {
    1821             :                                         installed = nil;
    1822             :                                 }
    1823             :                         }
    1824             :                 }
    1825             : 
    1826             :                 NSString *installedVersion = DESC(@"oolite-oxzmanager-version-none");
    1827             :                 if (installed != nil)
    1828             :                 {
    1829             :                         installedVersion = [installed oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-version-none")];
    1830             :                 }
    1831             : 
    1832             :                 /* If the filter is in use, the available_version key will
    1833             :                  * contain the version which can be downloaded. */
    1834             :                 [gui setArray:[NSArray arrayWithObjects:
    1835             :                          [manifest oo_stringForKey:kOOManifestCategory defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    1836             :                          [manifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    1837             :                          installedVersion,
    1838             :                          [manifest oo_stringForKey:kOOManifestAvailableVersion defaultValue:[manifest oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-version-none")]],
    1839             :                   nil] forRow:row];
    1840             : 
    1841             :                 [gui setKey:[manifest oo_stringForKey:kOOManifestIdentifier] forRow:row];
    1842             :                 /* 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. */
    1843             :                 [gui setColor:[self colorForManifest:manifest] forRow:row];
    1844             : 
    1845             :                 if (row == [gui selectedRow])
    1846             :                 {
    1847             :                         oxzLineSelected = YES;
    1848             :                         
    1849             :                         [gui setText:[self installStatusForManifest:manifest] forRow:OXZ_GUI_ROW_LISTSTATUS];
    1850             :                         [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTSTATUS];
    1851             : 
    1852             :                         [gui addLongText:[[[manifest oo_stringForKey:kOOManifestDescription] componentsSeparatedByString:@"\n"] oo_stringAtIndex:0] startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    1853             :                         
    1854             :                         NSString *infoUrl = [manifest oo_stringForKey:kOOManifestInformationURL];
    1855             :                         if (infoUrl != nil)
    1856             :                         {
    1857             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-url"),infoUrl,nil] forRow:OXZ_GUI_ROW_LISTINFO1];
    1858             :                         }
    1859             :                         NSUInteger size = [manifest oo_unsignedIntForKey:kOOManifestFileSize defaultValue:0];
    1860             :                         NSString *updatedDesc = nil;
    1861             : 
    1862             :                         NSUInteger timestamp = [manifest oo_unsignedIntegerForKey:kOOManifestUploadDate defaultValue:0];
    1863             :                         if (timestamp > 0)
    1864             :                         {
    1865             :                                 // list of installable OXZs
    1866             :                                 NSDate *updated = [NSDate dateWithTimeIntervalSince1970:timestamp];
    1867             :                         
    1868             :                                 //keep only the first part of the date string description, which should be in YYYY-MM-DD format
    1869             :                                 updatedDesc = [[[updated description] componentsSeparatedByString:@" "] oo_stringAtIndex:0];
    1870             :                                 
    1871             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-size"),[self humanSize:size],DESC(@"oolite-oxzmanager-infoline-date"),updatedDesc,nil] forRow:OXZ_GUI_ROW_LISTINFO2];
    1872             :                         } 
    1873             :                         else if (size > 0)
    1874             :                         {
    1875             :                                 // list of installed/removable OXZs
    1876             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-infoline-size"),[self humanSize:size],nil] forRow:OXZ_GUI_ROW_LISTINFO2];
    1877             :                         }
    1878             :                         
    1879             : 
    1880             :                 }
    1881             :                 
    1882             : 
    1883             :                 row++;
    1884             :         }
    1885             : 
    1886             :         if (!oxzLineSelected)
    1887             :         {
    1888             :                 if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    1889             :                 {
    1890             :                         // installeD
    1891             :                         [gui addLongText:OOExpand(DESC(@"oolite-oxzmanager-installed-nonepicked")) startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    1892             :                 }
    1893             :                 else
    1894             :                 {
    1895             :                         // installeR
    1896             :                         [gui addLongText:OOExpand(DESC(@"oolite-oxzmanager-installer-nonepicked")) startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    1897             :                 }
    1898             :                 
    1899             :         }
    1900             : 
    1901             : 
    1902             :         return startRow;
    1903             : }
    1904             : 
    1905             : 
    1906           0 : - (BOOL) removeOXZ:(NSUInteger)item
    1907             : {
    1908             :         NSArray *remList = _filteredList;
    1909             :         if ([remList count] <= item)
    1910             :         {
    1911             :                 OOLog(kOOOXZDebugLog, @"Unable to remove item %lu as only %lu in list", (unsigned long)item, (unsigned long)[remList count]);
    1912             :                 return NO;
    1913             :         }
    1914             :         NSString *filename = [[remList objectAtIndex:item] oo_stringForKey:kOOManifestFilePath];
    1915             :         if (filename == nil)
    1916             :         {
    1917             :                 OOLog(kOOOXZDebugLog, @"Unable to remove item %lu as filename not found", (unsigned long)item);
    1918             :                 return NO;
    1919             :         }
    1920             : 
    1921             :         if (![[NSFileManager defaultManager] oo_removeItemAtPath:filename])
    1922             :         {
    1923             :                 OOLog(kOOOXZErrorLog, @"Unable to remove file %@", filename);
    1924             :                 return NO;
    1925             :         }
    1926             :         _changesMade = YES;
    1927             :         DESTROY(_managedList); // will need updating
    1928             :         _interfaceState = OXZ_STATE_REMOVING;
    1929             :         [self gui];
    1930             :         return YES;
    1931             : }
    1932             : 
    1933             : 
    1934           0 : - (NSArray *) removeOptions
    1935             : {
    1936             :         NSArray *remList = _filteredList;
    1937             :         if ([remList count] == 0)
    1938             :         {
    1939             :                 return nil;
    1940             :         }
    1941             :         NSUInteger start = _offset;
    1942             :         if (start >= [remList count])
    1943             :         {
    1944             :                 start = 0;
    1945             :                 _offset = 0;
    1946             :         }
    1947             :         NSUInteger end = start + OXZ_GUI_NUM_LISTROWS;
    1948             :         if (end > [remList count])
    1949             :         {
    1950             :                 end = [remList count];
    1951             :         }
    1952             :         return [remList subarrayWithRange:NSMakeRange(start,end-start)];
    1953             : }
    1954             : 
    1955             : 
    1956             : - (OOGUIRow) showRemoveOptions
    1957             : {
    1958             :         // shows the current installation options page
    1959             :         OOGUIRow startRow = OXZ_GUI_ROW_LISTPREV;
    1960             :         NSArray *options = [self removeOptions];
    1961             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    1962             :         if (options == nil)
    1963             :         {
    1964             :                 [gui addLongText:DESC(@"oolite-oxzmanager-nothing-removable") startingAtRow:OXZ_GUI_ROW_PROGRESS align:GUI_ALIGN_LEFT];
    1965             :                 return startRow;
    1966             :         }
    1967             : 
    1968             :         OOGUITabSettings tab_stops;
    1969             :         tab_stops[0] = 0;
    1970             :         tab_stops[1] = 100;
    1971             :         tab_stops[2] = 400;
    1972             :         [gui setTabStops:tab_stops];
    1973             :         
    1974             :         [gui setArray:[NSArray arrayWithObjects:DESC(@"oolite-oxzmanager-heading-category"),
    1975             :                                                    DESC(@"oolite-oxzmanager-heading-title"), 
    1976             :                                                    DESC(@"oolite-oxzmanager-heading-version"), 
    1977             :                                                                 nil] forRow:OXZ_GUI_ROW_LISTHEAD];
    1978             :         if (_offset > 0)
    1979             :         {
    1980             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTPREV];
    1981             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"",@" <-- ", nil] forRow:OXZ_GUI_ROW_LISTPREV];
    1982             :                 [gui setKey:@"_BACK" forRow:OXZ_GUI_ROW_LISTPREV];
    1983             :         }
    1984             :         else
    1985             :         {
    1986             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
    1987             :                 {
    1988             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    1989             :                 }
    1990             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTPREV align:GUI_ALIGN_LEFT];
    1991             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTPREV];
    1992             :         }
    1993             :         if (_offset + OXZ_GUI_NUM_LISTROWS < [[self managedOXZs] count])
    1994             :         {
    1995             :                 [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTNEXT];
    1996             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"",@" --> ", nil] forRow:OXZ_GUI_ROW_LISTNEXT];
    1997             :                 [gui setKey:@"_NEXT" forRow:OXZ_GUI_ROW_LISTNEXT];
    1998             :         }
    1999             :         else
    2000             :         {
    2001             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
    2002             :                 {
    2003             :                         [gui setSelectedRow:OXZ_GUI_ROW_LISTSTART];
    2004             :                 }
    2005             :                 [gui setText:@"" forRow:OXZ_GUI_ROW_LISTNEXT align:GUI_ALIGN_LEFT];
    2006             :                 [gui setKey:GUI_KEY_SKIP forRow:OXZ_GUI_ROW_LISTNEXT];
    2007             :         }
    2008             : 
    2009             :         // clear any previous longtext
    2010             :         for (NSUInteger i = OXZ_GUI_ROW_LISTDESC; i < OXZ_GUI_ROW_INSTALL-1; i++)
    2011             :         {
    2012             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    2013             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    2014             :         }
    2015             :         // and any previous listed entries
    2016             :         for (NSUInteger i = OXZ_GUI_ROW_LISTSTART; i < OXZ_GUI_ROW_LISTNEXT; i++)
    2017             :         {
    2018             :                 [gui setText:@"" forRow:i align:GUI_ALIGN_LEFT];
    2019             :                 [gui setKey:GUI_KEY_SKIP forRow:i];
    2020             :         }
    2021             : 
    2022             : 
    2023             :         OOGUIRow row = OXZ_GUI_ROW_LISTSTART;
    2024             :         NSDictionary *manifest = nil;
    2025             :         BOOL oxzSelected = NO;
    2026             : 
    2027             :         foreach (manifest, options)
    2028             :         {
    2029             : 
    2030             :                 [gui setArray:[NSArray arrayWithObjects:
    2031             :                                                                    [manifest oo_stringForKey:kOOManifestCategory defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    2032             :                                                            [manifest oo_stringForKey:kOOManifestTitle defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    2033             :                                                            [manifest oo_stringForKey:kOOManifestVersion defaultValue:DESC(@"oolite-oxzmanager-missing-field")],
    2034             :                                                                         nil] forRow:row];
    2035             :                 NSString *identifier = [manifest oo_stringForKey:kOOManifestIdentifier];
    2036             :                 [gui setKey:identifier forRow:row];
    2037             :                 
    2038             :                 [gui setColor:[self colorForManifest:manifest] forRow:row];
    2039             :                 
    2040             :                 if (row == [gui selectedRow])
    2041             :                 {
    2042             :                         [gui setText:[self installStatusForManifest:manifest] forRow:OXZ_GUI_ROW_LISTSTATUS];
    2043             :                         [gui setColor:[OOColor greenColor] forRow:OXZ_GUI_ROW_LISTSTATUS];
    2044             : 
    2045             :                         [gui addLongText:[[[manifest oo_stringForKey:kOOManifestDescription] componentsSeparatedByString:@"\n"] oo_stringAtIndex:0] startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    2046             :                         
    2047             :                         oxzSelected = YES;
    2048             :                 }
    2049             :                 row++;
    2050             :         }
    2051             : 
    2052             :         if (!oxzSelected)
    2053             :         {
    2054             :                 [gui addLongText:DESC(@"oolite-oxzmanager-remover-nonepicked") startingAtRow:OXZ_GUI_ROW_LISTDESC align:GUI_ALIGN_LEFT];
    2055             :         }
    2056             : 
    2057             :         return startRow;        
    2058             : }
    2059             : 
    2060             : 
    2061             : - (void) showOptionsUpdate
    2062             : {
    2063             : 
    2064             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL)
    2065             :         {
    2066             :                 [self setFilteredList:[self applyCurrentFilter:_oxzList]];
    2067             :                 [self showInstallOptions];
    2068             :         }
    2069             :         else if (_interfaceState == OXZ_STATE_PICK_INSTALLED)
    2070             :         {
    2071             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    2072             :                 [self showInstallOptions];
    2073             :         }
    2074             :         else if (_interfaceState == OXZ_STATE_PICK_REMOVE)
    2075             :         {
    2076             :                 [self setFilteredList:[self applyCurrentFilter:[self managedOXZs]]];
    2077             :                 [self showRemoveOptions];
    2078             :         }
    2079             :         // else nothing necessary
    2080             : }
    2081             : 
    2082             : 
    2083             : - (void) showOptionsPrev
    2084             : {
    2085             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    2086             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_PICK_INSTALLED)
    2087             :         {
    2088             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTPREV)
    2089             :                 {
    2090             :                         [self processSelection];
    2091             :                 }
    2092             :         }
    2093             : }
    2094             : 
    2095             : 
    2096             : - (void) processOptionsPrev
    2097             : {
    2098             :         if (_offset < OXZ_GUI_NUM_LISTROWS)  
    2099             :         {
    2100             :                 _offset = 0;
    2101             :         }
    2102             :         else
    2103             :         {
    2104             :                 _offset -= OXZ_GUI_NUM_LISTROWS;
    2105             :         }
    2106             :         [self showOptionsUpdate];
    2107             : }
    2108             : 
    2109             : 
    2110             : - (void) processOptionsNext
    2111             : {
    2112             :         if (_offset + OXZ_GUI_NUM_LISTROWS < [_filteredList count])
    2113             :         {
    2114             :                 _offset += OXZ_GUI_NUM_LISTROWS;
    2115             :         }
    2116             :         [self showOptionsUpdate];
    2117             :         return;
    2118             : }
    2119             : 
    2120             : 
    2121             : - (void) showOptionsNext
    2122             : {
    2123             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    2124             :         if (_interfaceState == OXZ_STATE_PICK_INSTALL || _interfaceState == OXZ_STATE_PICK_REMOVE || _interfaceState == OXZ_STATE_PICK_INSTALLED)
    2125             :         {
    2126             :                 if ([gui selectedRow] == OXZ_GUI_ROW_LISTNEXT)
    2127             :                 {
    2128             :                         [self processSelection];
    2129             :                 }
    2130             :         }
    2131             : }
    2132             : 
    2133             : 
    2134           0 : - (NSString *) extractOXZ:(NSUInteger)item
    2135             : {
    2136             :         NSFileManager *fmgr                     = [NSFileManager defaultManager];
    2137             :         NSMutableString *extractionLog  = [[NSMutableString alloc] init];
    2138             :         NSDictionary *manifest                  = [_filteredList oo_dictionaryAtIndex:item];
    2139             :         NSString *version                               = [manifest oo_stringForKey:kOOManifestVersion];
    2140             :         NSString *identifier                    = [manifest oo_stringForKey:kOOManifestIdentifier];
    2141             :         NSString *path                                  = [self extractionBasePathForIdentifier:identifier andVersion:version];
    2142             : 
    2143             :         // OXZ errors should really never happen unless someone is messing
    2144             :         // directly with the managed folder while Oolite is running, but
    2145             :         // it's possible.
    2146             : 
    2147             :         NSString *oxzfile = [manifest oo_stringForKey:kOOManifestFilePath];
    2148             :         if (![fmgr fileExistsAtPath:oxzfile])
    2149             :         {
    2150             :                 OOLog(kOOOXZErrorLog,@"OXZ %@ could not be found",oxzfile);
    2151             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-no-original")];
    2152             :                 return [extractionLog autorelease];
    2153             :         }
    2154             :         const char* zipname = [oxzfile UTF8String];
    2155             :         unzFile uf = NULL;
    2156             :         uf = unzOpen64(zipname);
    2157             :         if (uf == NULL)
    2158             :         {
    2159             :                 OOLog(kOOOXZErrorLog,@"Could not open .oxz at %@ as zip file",path);
    2160             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-bad-original")];
    2161             :                 return [extractionLog autorelease];
    2162             :         }       
    2163             : 
    2164             :         if ([fmgr fileExistsAtPath:path])
    2165             :         {
    2166             :                 OOLog(kOOOXZErrorLog,@"Path %@ already exists",path);
    2167             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-exists")];
    2168             :                 unzClose(uf);
    2169             :                 return [extractionLog autorelease];
    2170             :         }
    2171             :         if (![fmgr oo_createDirectoryAtPath:path attributes:nil])
    2172             :         {
    2173             :                 OOLog(kOOOXZErrorLog,@"Path %@ could not be created",path);
    2174             :                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-unmakeable")];
    2175             :                 unzClose(uf);
    2176             :                 return [extractionLog autorelease];
    2177             :         }
    2178             :         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-main-created")];
    2179             :         NSUInteger counter = 0;
    2180             :         char rawComponentName[512];
    2181             :         BOOL error = NO;
    2182             :         unz_file_info64 file_info = {0};
    2183             :         if (unzGoToFirstFile(uf) == UNZ_OK)
    2184             :         {
    2185             :                 do 
    2186             :                 {
    2187             :                         unzGetCurrentFileInfo64(uf, &file_info,
    2188             :                                                                         rawComponentName, 512,
    2189             :                                                                         NULL, 0,
    2190             :                                                                         NULL, 0);
    2191             :                         NSString *componentName = [NSString stringWithUTF8String:rawComponentName];
    2192             :                         if ([componentName hasSuffix:@"/"])
    2193             :                         {
    2194             :                                 // folder
    2195             :                                 if (![fmgr oo_createDirectoryAtPath:[path stringByAppendingPathComponent:componentName] attributes:nil])
    2196             :                                 {
    2197             :                                         OOLog(kOOOXZErrorLog,@"Subpath %@ could not be created",componentName);
    2198             :                                         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2199             :                                         error = YES;
    2200             :                                         break;
    2201             :                                 }
    2202             :                                 else
    2203             :                                 {
    2204             :                                         OOLog(kOOOXZDebugLog,@"Subpath %@ created OK",componentName);
    2205             :                                 }
    2206             :                         }
    2207             :                         else
    2208             :                         {
    2209             :                                 // file
    2210             :                                 // usually folder must now exist, but just in case...
    2211             :                                 NSString *folder = [[path stringByAppendingPathComponent:componentName] stringByDeletingLastPathComponent];
    2212             :                                 if ([folder length] > 0 && ![fmgr fileExistsAtPath:folder] && ![fmgr oo_createDirectoryAtPath:folder attributes:nil])
    2213             :                                 {
    2214             :                                         OOLog(kOOOXZErrorLog,@"Subpath %@ could not be created",folder);
    2215             :                                         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2216             :                                         error = YES;
    2217             :                                         break;
    2218             :                                 }
    2219             :                                 
    2220             : 
    2221             :                                 // This is less efficient in memory use than just
    2222             :                                 // streaming out of the ZIP file onto disk
    2223             :                                 // but it makes error handling easier
    2224             :                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    2225             :                                 NSData *tmp = [NSData oo_dataWithOXZFile:[oxzfile stringByAppendingPathComponent:componentName]];
    2226             :                                 if (tmp == nil)
    2227             :                                 {
    2228             :                                         OOLog(kOOOXZErrorLog,@"Sub file %@ could not be extracted from the OXZ",componentName);
    2229             :                                         [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2230             :                                         error = YES;
    2231             :                                         [pool release];
    2232             :                                         break;
    2233             :                                 }
    2234             :                                 else
    2235             :                                 {
    2236             :                                         if (![tmp writeToFile:[path stringByAppendingPathComponent:componentName] atomically:YES])
    2237             :                                         {
    2238             :                                                 OOLog(kOOOXZErrorLog,@"Sub file %@ could not be created",componentName);
    2239             :                                                 [extractionLog appendString:DESC(@"oolite-oxzmanager-extract-log-sub-failed")];
    2240             :                                                 error = YES;
    2241             :                                                 [pool release];
    2242             :                                                 break;
    2243             :                                         }
    2244             :                                         else
    2245             :                                         {
    2246             :                                                 ++counter;
    2247             :                                         }
    2248             :                                 }
    2249             :                                 [pool release];
    2250             : 
    2251             :                         }
    2252             :                 }
    2253             :                 while (unzGoToNextFile(uf) == UNZ_OK);
    2254             :         }
    2255             :         unzClose(uf);
    2256             : 
    2257             :         if (!error)
    2258             :         {
    2259             :                 [extractionLog appendFormat:DESC(@"oolite-oxzmanager-extract-log-num-u-extracted"),counter];
    2260             :                 [extractionLog appendFormat:DESC(@"oolite-oxzmanager-extract-log-extracted-to-@"),path];
    2261             :         }
    2262             : 
    2263             :         return [extractionLog autorelease];
    2264             : }
    2265             : 
    2266             : 
    2267             : 
    2268             : 
    2269           0 : - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    2270             : {
    2271             :         _downloadStatus = OXZ_DOWNLOAD_RECEIVING;
    2272             :         OOLog(kOOOXZDebugLog, @"%@", @"Download receiving");
    2273             :         _downloadExpected = [response expectedContentLength];
    2274             :         _downloadProgress = 0;
    2275             :         DESTROY(_fileWriter);
    2276             :         [[NSFileManager defaultManager] createFileAtPath:[self downloadPath] contents:nil attributes:nil];
    2277             :         _fileWriter = [[NSFileHandle fileHandleForWritingAtPath:[self downloadPath]] retain];
    2278             :         if (_fileWriter == nil)
    2279             :         {
    2280             :                 // file system is full or read-only or something
    2281             :                 OOLog(kOOOXZErrorLog, @"%@", @"Unable to create download file");
    2282             :                 [self cancelUpdate];
    2283             :         }
    2284             : }
    2285             : 
    2286             : 
    2287           0 : - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    2288             : {
    2289             :         OOLog(kOOOXZDebugLog,@"Downloaded %llu bytes",[data length]);
    2290             :         [_fileWriter seekToEndOfFile];
    2291             :         [_fileWriter writeData:data];
    2292             :         _downloadProgress += [data length];
    2293             :         [self gui]; // update GUI
    2294             : #if OOLITE_WINDOWS
    2295             :         /* Irritating fix to issue https://github.com/OoliteProject/oolite/issues/95
    2296             :          *
    2297             :          * The problem is that on MINGW, GNUStep makes all socket streams
    2298             :          * blocking, which causes problems with the run loop. Calling this
    2299             :          * method of the run loop forces it to execute all already
    2300             :          * scheduled items with a time in the past, before any more items
    2301             :          * are placed on it, which means that the main game update gets a
    2302             :          * chance to run.
    2303             :          *
    2304             :          * This stops the interface freezing - and Oolite appearing to
    2305             :          * have stopped responding to the OS - when downloading large
    2306             :          * (>20Mb) OXZ files.
    2307             :          *
    2308             :          * CIM 6 July 2014
    2309             :          */
    2310             :         [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
    2311             : #endif
    2312             : }
    2313             : 
    2314             : 
    2315           0 : - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    2316             : {
    2317             :         _downloadStatus = OXZ_DOWNLOAD_COMPLETE;
    2318             :         OOLog(kOOOXZDebugLog, @"%@", @"Download complete");
    2319             :         [_fileWriter synchronizeFile];
    2320             :         [_fileWriter closeFile];
    2321             :         DESTROY(_fileWriter);
    2322             :         DESTROY(_currentDownload);
    2323             :         if (_interfaceState == OXZ_STATE_UPDATING)
    2324             :         {
    2325             :                 if (![self processDownloadedManifests])
    2326             :                 {
    2327             :                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2328             :                 }
    2329             :         }
    2330             :         else if (_interfaceState == OXZ_STATE_INSTALLING)
    2331             :         {
    2332             :                 if (![self processDownloadedOXZ])
    2333             :                 {
    2334             :                         _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2335             :                 }
    2336             :         }
    2337             :         else
    2338             :         {
    2339             :                 OOLog(kOOOXZErrorLog,@"Error: download completed in unexpected state %d. This is an internal error - please report it.",_interfaceState);
    2340             :                 _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2341             :         }
    2342             : }
    2343             : 
    2344             : 
    2345           0 : - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    2346             : {
    2347             :         _downloadStatus = OXZ_DOWNLOAD_ERROR;
    2348             :         OOLog(kOOOXZErrorLog,@"Error downloading file: %@",[error description]);
    2349             :         [_fileWriter closeFile];
    2350             :         DESTROY(_fileWriter);
    2351             :         DESTROY(_currentDownload);
    2352             : }
    2353             : 
    2354             : 
    2355             : 
    2356             : 
    2357             : @end
    2358             : 
    2359             : /* Sort by category, then title, then version - and that should be unique */
    2360           0 : NSComparisonResult oxzSort(id m1, id m2, void *context)
    2361             : {
    2362             :         NSComparisonResult result = [[m1 oo_stringForKey:kOOManifestCategory defaultValue:@"zz"] localizedCompare:[m2 oo_stringForKey:kOOManifestCategory defaultValue:@"zz"]];
    2363             :         if (result == NSOrderedSame)
    2364             :         {
    2365             :                 result = [[m1 oo_stringForKey:kOOManifestTitle defaultValue:@"zz"] localizedCompare:[m2 oo_stringForKey:kOOManifestTitle defaultValue:@"zz"]];
    2366             :                 if (result == NSOrderedSame)
    2367             :                 {
    2368             :                         result = [[m2 oo_stringForKey:kOOManifestVersion defaultValue:@"0"] localizedCompare:[m1 oo_stringForKey:kOOManifestVersion defaultValue:@"0"]];
    2369             :                 }
    2370             :         }
    2371             :         return result;
    2372             : }

Generated by: LCOV version 1.14