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

Generated by: LCOV version 1.14