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