Oolite 1.91.0.7644-241112-7f5034b
Loading...
Searching...
No Matches
OOOXZManager.m
Go to the documentation of this file.
1/*
2
3OOOXZManager.m
4
5Responsible for installing and uninstalling OXZs
6
7Oolite
8Copyright (C) 2004-2013 Giles C Williams and contributors
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23MA 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"
40#import "OOColor.h"
41#import "OOStringExpander.h"
42#import "MyOpenGLView.h"
43
44#import "unzip.h"
45
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";*/
51static NSString * const kOOOXZDataURL = @"http://addons.oolite.space/api/1.0/overview";
52/* The config parameter to use a non-default URL at runtime */
53static NSString * const kOOOXZDataConfig = @"oxz-index-url";
54/* The filename to store the downloaded manifest.plist array */
55static 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; */
57static NSString * const kOOOXZTmpPath = @"Oolite-download.oxz";
58/* The filename to temporarily store the downloaded plists. */
59static NSString * const kOOOXZTmpPlistPath = @"Oolite-download.plist";
60
61/* Log file record types */
62static NSString * const kOOOXZErrorLog = @"oxz.manager.error";
63static NSString * const kOOOXZDebugLog = @"oxz.manager.debug";
64
65
66/* Filter components */
67static NSString * const kOOOXZFilterAll = @"*";
68static NSString * const kOOOXZFilterUpdates = @"u";
69static NSString * const kOOOXZFilterInstallable = @"i";
70static NSString * const kOOOXZFilterKeyword = @"k:";
71static NSString * const kOOOXZFilterAuthor = @"a:";
72static NSString * const kOOOXZFilterCategory = @"c:";
73static NSString * const kOOOXZFilterDays = @"d:";
74static NSString * const kOOOXZFilterTag = @"t:";
75
76
77typedef enum {
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
89
90
91enum {
116
117
118NSComparisonResult oxzSort(id m1, id m2, void *context);
119
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- (NSString *) manifestPath;
130- (NSString *) downloadPath;
131- (NSString *) extractionBasePathForIdentifier:(NSString *)identifier andVersion:(NSString *)version;
132- (NSString *) dataURL;
133- (NSString *) humanSize:(NSUInteger)bytes;
134
135- (BOOL) ensureInstallPath;
136
137- (BOOL) beginDownload:(NSMutableURLRequest *)request;
138- (BOOL) processDownloadedManifests;
139- (BOOL) processDownloadedOXZ;
140
141- (OXZInstallableState) installableState:(NSDictionary *)manifest;
142- (OOColor *) colorForManifest:(NSDictionary *)manifest;
143- (NSString *) installStatusForManifest:(NSDictionary *)manifest;
144
145- (BOOL) validateFilter:(NSString *)input;
146
147- (void) setOXZList:(NSArray *)list;
148- (void) setFilteredList:(NSArray *)list;
149- (NSArray *) applyCurrentFilter:(NSArray *)list;
150
151- (void) setCurrentDownload:(NSURLConnection *)download withLabel:(NSString *)label;
152- (void) setProgressStatus:(NSString *)newStatus;
153
154- (BOOL) installOXZ:(NSUInteger)item;
155- (BOOL) updateAllOXZ;
156- (BOOL) removeOXZ:(NSUInteger)item;
157- (NSArray *) installOptions;
158- (NSArray *) removeOptions;
159
160- (NSString *) extractOXZ:(NSUInteger)item;
161
162/* Delegates for URL downloader */
163- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
164- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
165- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
166- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
167
168@end
169
170@interface OOOXZManager (OOFilterRules)
171- (BOOL) applyFilterByNoFilter:(NSDictionary *)manifest;
172- (BOOL) applyFilterByUpdateRequired:(NSDictionary *)manifest;
173- (BOOL) applyFilterByInstallable:(NSDictionary *)manifest;
174- (BOOL) applyFilterByKeyword:(NSDictionary *)manifest keyword:(NSString *)keyword;
175- (BOOL) applyFilterByAuthor:(NSDictionary *)manifest author:(NSString *)author;
176- (BOOL) applyFilterByDays:(NSDictionary *)manifest days:(NSString *)days;
177- (BOOL) applyFilterByTag:(NSDictionary *)manifest tag:(NSString *)tag;
178- (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- (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- (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- (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- (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- (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- (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- (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- (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- (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- (void) setFilteredList:(NSArray *)list
357{
358 DESTROY(_filteredList);
359 _filteredList = [list copy]; // copy retains
360}
361
362
363- (void) setFilter:(NSString *)filter
364{
365 DESTROY(_currentFilter);
366 _currentFilter = [[filter lowercaseString] copy]; // copy retains
367}
368
369
370- (NSArray *) applyCurrentFilter:(NSArray *)list
371{
372 SEL filterSelector = @selector(applyFilterByNoFilter:);
373 NSString *parameter = nil;
374 if ([_currentFilter isEqualToString:kOOOXZFilterUpdates])
375 {
376 filterSelector = @selector(applyFilterByUpdateRequired:);
377 }
378 else if ([_currentFilter isEqualToString:kOOOXZFilterInstallable])
379 {
380 filterSelector = @selector(applyFilterByInstallable:);
381 }
382 else if ([_currentFilter hasPrefix:kOOOXZFilterKeyword])
383 {
384 filterSelector = @selector(applyFilterByKeyword:keyword:);
385 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterKeyword length]];
386 }
387 else if ([_currentFilter hasPrefix:kOOOXZFilterAuthor])
388 {
389 filterSelector = @selector(applyFilterByAuthor:author:);
390 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterAuthor length]];
391 }
392 else if ([_currentFilter hasPrefix:kOOOXZFilterDays])
393 {
394 filterSelector = @selector(applyFilterByDays:days:);
395 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterDays length]];
396 }
397 else if ([_currentFilter hasPrefix:kOOOXZFilterTag])
398 {
399 filterSelector = @selector(applyFilterByTag:tag:);
400 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterTag length]];
401 }
402 else if ([_currentFilter hasPrefix:kOOOXZFilterCategory])
403 {
404 filterSelector = @selector(applyFilterByCategory:category:);
405 parameter = [_currentFilter substringFromIndex:[kOOOXZFilterCategory length]];
406 }
407
408 NSMutableArray *filteredList = [NSMutableArray arrayWithCapacity:[list count]];
409 NSDictionary *manifest = nil;
410 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:filterSelector]];
411 [invocation setSelector:filterSelector];
412 [invocation setTarget:self];
413 if (parameter != nil)
414 {
415 [invocation setArgument:&parameter atIndex:3];
416 }
417
418 foreach(manifest, list)
419 {
420 [invocation setArgument:&manifest atIndex:2];
421 [invocation invoke];
422 BOOL filterAccepted = NO;
423 [invocation getReturnValue:&filterAccepted];
424 if (filterAccepted)
425 {
426 [filteredList addObject:manifest];
427 }
428 }
429 // any bad filter that gets this far is also treated as '*'
430 // so don't need to explicitly test for '*' or ''
431 return [[filteredList copy] autorelease];
432}
433
434
435/*** Start filters ***/
436- (BOOL) applyFilterByNoFilter:(NSDictionary *)manifest
437{
438 return YES;
439}
440
441
442- (BOOL) applyFilterByUpdateRequired:(NSDictionary *)manifest
443{
444 return ([self installableState:manifest] == OXZ_INSTALLABLE_UPDATE);
445}
446
447
448- (BOOL) applyFilterByInstallable:(NSDictionary *)manifest
449{
450 return ([self installableState:manifest] < OXZ_UNINSTALLABLE_ALREADY);
451}
452
453
454- (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- (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- (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- (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- (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- (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- (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- (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- (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
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- (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- (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
807
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- (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- (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 {
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
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
1023 }
1024 else if ([installed oo_stringForKey:kOOManifestAvailableVersion defaultValue:nil] == nil)
1025 {
1026 // installed, but no remote copy is indexed any more
1028 }
1029 }
1030 /* Check for dependencies being met */
1031 if ([ResourceManager manifestHasConflicts:manifest logErrors:NO])
1032 {
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
1048 }
1050 }
1051 if ([ResourceManager manifestHasMissingDependencies:manifest logErrors:NO])
1052 {
1054 }
1055 return OXZ_INSTALLABLE_OKAY;
1056}
1057
1058
1059- (OOColor *) colorForManifest:(NSDictionary *)manifest
1060{
1061 switch ([self installableState:manifest])
1062 {
1064 return [OOColor yellowColor];
1066 return [OOColor cyanColor];
1068 return [OOColor orangeColor];
1070 return [OOColor brownColor];
1072 return [OOColor whiteColor];
1074 return [OOColor redColor];
1076 return [OOColor grayColor];
1078 return [OOColor blueColor];
1079 }
1080 return [OOColor yellowColor]; // never
1081}
1082
1083
1084- (NSString *) installStatusForManifest:(NSDictionary *)manifest
1085{
1086 switch ([self installableState:manifest])
1087 {
1089 return DESC(@"oolite-oxzmanager-installable-okay");
1091 return DESC(@"oolite-oxzmanager-installable-update");
1093 return DESC(@"oolite-oxzmanager-installable-depend");
1095 return DESC(@"oolite-oxzmanager-installable-conflicts");
1097 return DESC(@"oolite-oxzmanager-installable-already");
1099 return DESC(@"oolite-oxzmanager-installable-manual");
1101 return DESC(@"oolite-oxzmanager-installable-version");
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 */
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 {
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;
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
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:
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;
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;
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
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- (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- (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- (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
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- (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- (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
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- (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- (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- (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- (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- (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 */
2358NSComparisonResult 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}
OOGUITabStop OOGUITabSettings[GUI_MAX_COLUMNS]
NSInteger OOGUIRow
#define DESTROY(x)
Definition OOCocoa.h:77
static OODebugMonitor * sSingleton
#define EXPECT_NOT(x)
#define OOLog(class, format,...)
Definition OOLogging.h:88
static NSString *const kOOManifestAvailableVersion
static NSString *const kOOManifestRelationIdentifier
static NSString *const kOOManifestIdentifier
static NSString *const kOOManifestVersion
static NSString *const kOOManifestFilePath
@ OXZ_STATE_UPDATING
@ OXZ_STATE_INSTALLING
@ OXZ_STATE_PICK_INSTALLED
@ OXZ_STATE_RESTARTING
@ OXZ_STATE_SETFILTER
@ OXZ_STATE_NODATA
@ OXZ_STATE_TASKDONE
@ OXZ_STATE_DEPENDENCIES
@ OXZ_STATE_REMOVING
@ OXZ_STATE_PICK_INSTALL
@ OXZ_STATE_PICK_REMOVE
@ OXZ_STATE_EXTRACTDONE
@ OXZ_STATE_MAIN
@ OXZ_STATE_EXTRACT
@ OXZ_DOWNLOAD_RECEIVING
@ OXZ_DOWNLOAD_COMPLETE
@ OXZ_DOWNLOAD_STARTED
@ OXZ_DOWNLOAD_NONE
@ OXZ_DOWNLOAD_ERROR
static NSString *const kOOOXZFilterDays
static NSString *const kOOOXZErrorLog
static NSString *const kOOOXZFilterTag
static NSString *const kOOOXZFilterKeyword
OXZInstallableState
@ OXZ_INSTALLABLE_UPDATE
@ OXZ_INSTALLABLE_DEPENDENCIES
@ OXZ_INSTALLABLE_CONFLICTS
@ OXZ_INSTALLABLE_OKAY
@ OXZ_UNINSTALLABLE_VERSION
@ OXZ_UNINSTALLABLE_ALREADY
@ OXZ_UNINSTALLABLE_NOREMOTE
@ OXZ_UNINSTALLABLE_MANUAL
static NSString *const kOOOXZManifestCache
static NSString *const kOOOXZFilterUpdates
static NSString *const kOOOXZFilterAll
static NSString *const kOOOXZFilterCategory
static NSString *const kOOOXZFilterInstallable
static NSString *const kOOOXZTmpPlistPath
static NSString *const kOOOXZFilterAuthor
NSComparisonResult oxzSort(id m1, id m2, void *context)
static NSString *const kOOOXZDataURL
static NSString *const kOOOXZTmpPath
static NSString *const kOOOXZDataConfig
static OOOXZManager * sSingleton
static NSString *const kOOOXZDebugLog
@ OXZ_GUI_ROW_CANCEL
@ OXZ_GUI_ROW_PROGRESS
@ OXZ_GUI_ROW_FILTERHELP
@ OXZ_GUI_ROW_FIRSTRUN
@ OXZ_GUI_NUM_LISTROWS
@ OXZ_GUI_ROW_LISTINFO2
@ OXZ_GUI_ROW_REMOVE
@ OXZ_GUI_ROW_LISTFILTER
@ OXZ_GUI_ROW_EXIT
@ OXZ_GUI_ROW_INSTALL
@ OXZ_GUI_ROW_FILTERCURRENT
@ OXZ_GUI_ROW_LISTSTATUS
@ OXZ_GUI_ROW_INSTALLED
@ OXZ_GUI_ROW_LISTINFO1
@ OXZ_GUI_ROW_LISTDESC
@ OXZ_GUI_ROW_INPUT
@ OXZ_GUI_ROW_UPDATE_ALL
@ OXZ_GUI_ROW_PROCEED
@ OXZ_GUI_ROW_LISTHEAD
@ OXZ_GUI_ROW_LISTSTART
@ OXZ_GUI_ROW_LISTPREV
@ OXZ_GUI_ROW_LISTNEXT
@ OXZ_GUI_ROW_UPDATE
NSDictionary * OODictionaryFromFile(NSString *path)
unsigned count
return nil
NSArray * ComponentsFromVersionString(NSString *string)
NSComparisonResult CompareVersions(NSArray *version1, NSArray *version2)
#define DESC(key)
Definition Universe.h:839
BOOL setSelectedRow:(OOGUIRow row)
OOGUIRow addLongText:startingAtRow:align:(NSString *str,[startingAtRow] OOGUIRow row,[align] OOGUIAlignment alignment)
void setText:forRow:(NSString *str,[forRow] OOGUIRow row)
void setText:forRow:align:(NSString *str,[forRow] OOGUIRow row,[align] OOGUIAlignment alignment)
void clearAndKeepBackground:(BOOL keepBackground)
OOGUIRow selectedRow
void setSelectableRange:(NSRange range)
void setColor:forRow:(OOColor *color,[forRow] OOGUIRow row)
void setTitle:(NSString *str)
void setTabStops:(OOGUITabSettings stops)
void setArray:forRow:(NSArray *arr,[forRow] OOGUIRow row)
void setKey:forRow:(NSString *str,[forRow] OOGUIRow row)
NSString * cacheDirectoryPathCreatingIfNecessary:(BOOL create)
OOCacheManager * sharedCache()
OOColor * cyanColor()
Definition OOColor.m:286
OOColor * orangeColor()
Definition OOColor.m:304
OOColor * redColor()
Definition OOColor.m:268
OOColor * greenColor()
Definition OOColor.m:274
OOColor * blueColor()
Definition OOColor.m:280
OOColor * grayColor()
Definition OOColor.m:262
OOColor * whiteColor()
Definition OOColor.m:256
OOColor * brownColor()
Definition OOColor.m:316
OOColor * yellowColor()
Definition OOColor.m:292
void resetManifestKnowledgeForOXZManager()
NSDictionary * manifestForIdentifier:(NSString *identifier)
NSArray * userRootPaths()
voidpf void uLong size
Definition ioapi.h:134
const char * filename
Definition ioapi.h:133
int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)
Definition unzip.c:1130
int ZEXPORT unzGoToFirstFile(unzFile file)
Definition unzip.c:1184
unzFile ZEXPORT unzOpen64(const void *path)
Definition unzip.c:801
int ZEXPORT unzGoToNextFile(unzFile file)
Definition unzip.c:1205
int ZEXPORT unzClose(unzFile file)
Definition unzip.c:811
voidp unzFile
Definition unzip.h:70
#define UNZ_OK
Definition unzip.h:74