]> git.saurik.com Git - cydia.git/blame - Cydia.mm
Fundamental performance improvements.
[cydia.git] / Cydia.mm
CommitLineData
36bb2ca2
JF
1/* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
3*/
4
5/*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
17 * distribution.
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36*/
37
dc5812ec 38/* #include Directives {{{ */
807ae6d7
JF
39#import "UICaboodle.h"
40
ab398adf
JF
41#include <objc/objc.h>
42#include <objc/runtime.h>
43
5f6bff8c 44#include <CoreGraphics/CoreGraphics.h>
9743c015 45#include <GraphicsServices/GraphicsServices.h>
5f6bff8c 46#include <Foundation/Foundation.h>
7d2ac47f 47
7d2ac47f 48#import <QuartzCore/CALayer.h>
36bb2ca2 49
baf80942 50#import <UIKit/UIKit.h>
a9543575
JF
51
52// XXX: remove
c390d3ab 53#import <MessageUI/MailComposeController.h>
7e9a36b6 54
affeffc7
JF
55#import <WebCore/WebScriptObject.h>
56//#include <WebCore/DOMHTML.h>
57
36bb2ca2 58#include <WebKit/WebFrame.h>
affeffc7 59#include <WebKit/WebPolicyDelegate.h>
36bb2ca2 60#include <WebKit/WebView.h>
9743c015 61
affeffc7
JF
62#import <WebKit/WebView-WebPrivate.h>
63
2367a917 64#include <sstream>
a0be02eb
JF
65#include <string>
66
2367a917
JF
67#include <ext/stdio_filebuf.h>
68
dc5812ec
JF
69#include <apt-pkg/acquire.h>
70#include <apt-pkg/acquire-item.h>
71#include <apt-pkg/algorithms.h>
72#include <apt-pkg/cachefile.h>
77fcccaf 73#include <apt-pkg/clean.h>
dc5812ec 74#include <apt-pkg/configuration.h>
3178d79b 75#include <apt-pkg/debmetaindex.h>
dc5812ec
JF
76#include <apt-pkg/error.h>
77#include <apt-pkg/init.h>
dddbc481 78#include <apt-pkg/mmap.h>
dc5812ec 79#include <apt-pkg/pkgrecords.h>
dddbc481 80#include <apt-pkg/sha1.h>
dc5812ec 81#include <apt-pkg/sourcelist.h>
2367a917 82#include <apt-pkg/sptr.h>
affeffc7 83#include <apt-pkg/strutl.h>
dc5812ec 84
87f46a96
JF
85#include <sys/types.h>
86#include <sys/stat.h>
3178d79b 87#include <sys/sysctl.h>
87f46a96 88
2a8d9add 89#include <notify.h>
dddbc481 90#include <dlfcn.h>
3178d79b 91
b4d89997
JF
92extern "C" {
93#include <mach-o/nlist.h>
94}
95
a0be02eb
JF
96#include <cstdio>
97#include <cstdlib>
98#include <cstring>
b4d89997 99
2367a917 100#include <errno.h>
dc5812ec 101#include <pcre.h>
a9543575 102
a9543575
JF
103#import "BrowserView.h"
104#import "ResetView.h"
dc5812ec 105/* }}} */
3178d79b 106
affeffc7
JF
107//#define _finline __attribute__((force_inline))
108#define _finline inline
90515f88 109
807ae6d7
JF
110struct timeval _ltv;
111bool _itv;
112
113#define _limit(count) do { \
114 static size_t _count(0); \
115 if (++_count == count) \
116 exit(0); \
117} while (false)
118
affeffc7
JF
119/* Objective-C Handle<> {{{ */
120template <typename Type_>
121class _H {
122 typedef _H<Type_> This_;
1e7a90f5 123
affeffc7
JF
124 private:
125 Type_ *value_;
1e7a90f5 126
affeffc7
JF
127 _finline void Retain_() {
128 if (value_ != nil)
129 [value_ retain];
130 }
1e7a90f5 131
affeffc7
JF
132 _finline void Clear_() {
133 if (value_ != nil)
134 [value_ release];
135 }
1e7a90f5 136
affeffc7
JF
137 public:
138 _finline _H(Type_ *value = NULL, bool mended = false) :
139 value_(value)
140 {
141 if (!mended)
142 Retain_();
143 }
ffdee4ec 144
affeffc7
JF
145 _finline ~_H() {
146 Clear_();
147 }
1e7a90f5 148
affeffc7
JF
149 _finline This_ &operator =(Type_ *value) {
150 if (value_ != value) {
151 Clear_();
152 value_ = value;
153 Retain_();
154 } return this;
155 }
156};
157/* }}} */
158
159#define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
160
161void NSLogRect(const char *fix, const CGRect &rect) {
162 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
1e7a90f5
JF
163}
164
affeffc7 165static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
1e7a90f5 166
affeffc7
JF
167/* iPhoneOS 2.0 Compatibility {{{ */
168#ifdef __OBJC2__
1e7a90f5
JF
169@interface UITextView (iPhoneOS)
170- (void) setTextSize:(float)size;
171@end
172
173@implementation UITextView (iPhoneOS)
174
175- (void) setTextSize:(float)size {
176 [self setFont:[[self font] fontWithSize:size]];
177}
178
179@end
49048579 180#endif
1e7a90f5 181/* }}} */
49048579 182
c390d3ab
JF
183extern NSString * const kCAFilterNearest;
184
6e201c55
JF
185/* Information Dictionaries {{{ */
186@interface NSMutableArray (Cydia)
187- (void) addInfoDictionary:(NSDictionary *)info;
188@end
189
190@implementation NSMutableArray (Cydia)
191
192- (void) addInfoDictionary:(NSDictionary *)info {
193 [self addObject:info];
194}
195
196@end
197
198@interface NSMutableDictionary (Cydia)
199- (void) addInfoDictionary:(NSDictionary *)info;
200@end
201
202@implementation NSMutableDictionary (Cydia)
203
204- (void) addInfoDictionary:(NSDictionary *)info {
205 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
206 [self setObject:info forKey:bundle];
207}
208
affeffc7
JF
209@end
210/* }}} */
211/* Pop Transitions {{{ */
212@interface PopTransitionView : UITransitionView {
213}
214
215@end
216
217@implementation PopTransitionView
218
219- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
220 if (from != nil && to == nil)
221 [self removeFromSuperview];
222}
223
224@end
225
226@interface UIView (PopUpView)
227- (void) popFromSuperviewAnimated:(BOOL)animated;
228- (void) popSubview:(UIView *)view;
229@end
230
231@implementation UIView (PopUpView)
232
233- (void) popFromSuperviewAnimated:(BOOL)animated {
234 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
235}
236
237- (void) popSubview:(UIView *)view {
238 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
239 [transition setDelegate:transition];
240 [self addSubview:transition];
241
242 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
243 [transition transition:UITransitionNone toView:blank];
244 [transition transition:UITransitionPushFromBottom toView:view];
245}
246
6e201c55
JF
247@end
248/* }}} */
249
c390d3ab 250#define lprintf(args...) fprintf(stderr, args)
a9543575 251
affeffc7
JF
252#define ForSaurik 1
253#define RecycleWebViews 0
795d26fc 254
807ae6d7
JF
255/* Radix Sort {{{ */
256@interface NSMutableArray (Radix)
257- (void) radixUsingSelector:(SEL)selector withObject:(id)object;
258@end
259
260@implementation NSMutableArray (Radix)
261
262- (void) radixUsingSelector:(SEL)selector withObject:(id)object {
263 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
264 [invocation setSelector:selector];
265 [invocation setArgument:&object atIndex:2];
266
267 size_t count([self count]);
268
269 struct RadixItem {
270 size_t index;
271 uint32_t key;
272 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
273
274 for (size_t i(0); i != count; ++i) {
275 RadixItem &item(lhs[i]);
276 item.index = i;
277
278 id object([self objectAtIndex:i]);
279 [invocation setTarget:object];
280
281 [invocation invoke];
282 [invocation getReturnValue:&item.key];
283 }
284
285 static const size_t bits = 11;
286 static const size_t slots = 1 << bits;
287 static const size_t passes = (32 + (bits - 1)) / bits;
288
289 size_t *hist(new size_t[slots]);
290
291 for (size_t pass(0); pass != passes; ++pass) {
292 memset(hist, 0, sizeof(size_t) * slots);
293
294 for (size_t i(0); i != count; ++i) {
295 uint32_t key(lhs[i].key);
296 key >>= pass * bits;
297 key &= _not(uint32_t) >> 32 - bits;
298 ++hist[key];
299 }
300
301 size_t offset(0);
302 for (size_t i(0); i != slots; ++i) {
303 size_t local(offset);
304 offset += hist[i];
305 hist[i] = local;
306 }
307
308 for (size_t i(0); i != count; ++i) {
309 uint32_t key(lhs[i].key);
310 key >>= pass * bits;
311 key &= _not(uint32_t) >> 32 - bits;
312 rhs[hist[key]++] = lhs[i];
313 }
314
315 RadixItem *tmp(lhs);
316 lhs = rhs;
317 rhs = tmp;
318 }
319
320 delete [] hist;
321
322 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
323 for (size_t i(0); i != count; ++i)
324 [values addObject:[self objectAtIndex:lhs[i].index]];
325 [self setArray:values];
326
327 delete [] swap;
328}
329
330@end
331/* }}} */
332
31f3cfff
JF
333typedef enum {
334 kUIControlEventMouseDown = 1 << 0,
335 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
336 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
337 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
338 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
339 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
340} UIControlEventMasks;
341
2388b078
JF
342@interface NSString (UIKit)
343- (NSString *) stringByAddingPercentEscapes;
344- (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
345@end
346
347@interface NSString (Cydia)
348+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
a54b1c10 349- (NSComparisonResult) compareByPath:(NSString *)other;
2388b078
JF
350@end
351
352@implementation NSString (Cydia)
353
354+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
355 char data[length + 1];
356 memcpy(data, bytes, length);
357 data[length] = '\0';
358 return [NSString stringWithUTF8String:data];
359}
360
a54b1c10
JF
361- (NSComparisonResult) compareByPath:(NSString *)other {
362 NSString *prefix = [self commonPrefixWithString:other options:0];
363 size_t length = [prefix length];
364
365 NSRange lrange = NSMakeRange(length, [self length] - length);
366 NSRange rrange = NSMakeRange(length, [other length] - length);
367
368 lrange = [self rangeOfString:@"/" options:0 range:lrange];
369 rrange = [other rangeOfString:@"/" options:0 range:rrange];
370
371 NSComparisonResult value;
372
373 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
374 value = NSOrderedSame;
375 else if (lrange.location == NSNotFound)
376 value = NSOrderedAscending;
377 else if (rrange.location == NSNotFound)
378 value = NSOrderedDescending;
379 else
380 value = NSOrderedSame;
381
382 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
383 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
384 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
385 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
386
387 NSComparisonResult result = [lpath compare:rpath];
388 return result == NSOrderedSame ? value : result;
389}
390
2388b078
JF
391@end
392
b4d89997
JF
393/* Perl-Compatible RegEx {{{ */
394class Pcre {
395 private:
396 pcre *code_;
397 pcre_extra *study_;
398 int capture_;
399 int *matches_;
400 const char *data_;
401
402 public:
403 Pcre(const char *regex) :
404 study_(NULL)
405 {
406 const char *error;
407 int offset;
408 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
409
410 if (code_ == NULL) {
c390d3ab 411 lprintf("%d:%s\n", offset, error);
b4d89997
JF
412 _assert(false);
413 }
414
415 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
416 matches_ = new int[(capture_ + 1) * 3];
417 }
418
419 ~Pcre() {
420 pcre_free(code_);
421 delete matches_;
422 }
423
424 NSString *operator [](size_t match) {
2388b078 425 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
b4d89997
JF
426 }
427
8b29f8e6
JF
428 bool operator ()(NSString *data) {
429 // XXX: length is for characters, not for bytes
430 return operator ()([data UTF8String], [data length]);
431 }
432
b4d89997
JF
433 bool operator ()(const char *data, size_t size) {
434 data_ = data;
435 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
436 }
437};
438/* }}} */
36bb2ca2 439/* Mime Addresses {{{ */
36bb2ca2
JF
440@interface Address : NSObject {
441 NSString *name_;
6f1a15d9 442 NSString *address_;
36bb2ca2
JF
443}
444
445- (NSString *) name;
6f1a15d9 446- (NSString *) address;
36bb2ca2
JF
447
448+ (Address *) addressWithString:(NSString *)string;
449- (Address *) initWithString:(NSString *)string;
450@end
451
452@implementation Address
453
454- (void) dealloc {
455 [name_ release];
6f1a15d9
JF
456 if (address_ != nil)
457 [address_ release];
36bb2ca2
JF
458 [super dealloc];
459}
460
461- (NSString *) name {
462 return name_;
463}
464
6f1a15d9
JF
465- (NSString *) address {
466 return address_;
36bb2ca2
JF
467}
468
469+ (Address *) addressWithString:(NSString *)string {
470 return [[[Address alloc] initWithString:string] autorelease];
471}
472
6f1a15d9
JF
473+ (NSArray *) _attributeKeys {
474 return [NSArray arrayWithObjects:@"address", @"name", nil];
475}
476
477- (NSArray *) attributeKeys {
478 return [[self class] _attributeKeys];
479}
480
481+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
482 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
483}
484
36bb2ca2
JF
485- (Address *) initWithString:(NSString *)string {
486 if ((self = [super init]) != nil) {
487 const char *data = [string UTF8String];
488 size_t size = [string length];
489
6f1a15d9 490 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
7b0ce2da 491
6f1a15d9
JF
492 if (address_r(data, size)) {
493 name_ = [address_r[1] retain];
494 address_ = [address_r[2] retain];
36bb2ca2 495 } else {
affeffc7 496 name_ = [string retain];
6f1a15d9 497 address_ = nil;
36bb2ca2
JF
498 }
499 } return self;
500}
501
502@end
503/* }}} */
5f6bff8c 504/* CoreGraphics Primitives {{{ */
b4d89997
JF
505class CGColor {
506 private:
507 CGColorRef color_;
508
509 public:
36bb2ca2
JF
510 CGColor() :
511 color_(NULL)
512 {
513 }
514
515 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
516 color_(NULL)
517 {
518 Set(space, red, green, blue, alpha);
519 }
520
521 void Clear() {
522 if (color_ != NULL)
523 CGColorRelease(color_);
b4d89997
JF
524 }
525
526 ~CGColor() {
36bb2ca2
JF
527 Clear();
528 }
529
530 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
531 Clear();
532 float color[] = {red, green, blue, alpha};
533 color_ = CGColorCreate(space, color);
b4d89997
JF
534 }
535
536 operator CGColorRef() {
537 return color_;
538 }
539};
b4d89997
JF
540/* }}} */
541
f641a0e5
JF
542extern "C" void UISetColor(CGColorRef color);
543
36bb2ca2 544/* Random Global Variables {{{ */
3178d79b 545static const int PulseInterval_ = 50000;
2388b078 546static const int ButtonBarHeight_ = 48;
6d9712c4 547static const float KeyboardTime_ = 0.3f;
affeffc7
JF
548
549#define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
550#define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
2a8d9add 551
f641a0e5 552static CGColor Blue_;
77fcccaf 553static CGColor Blueish_;
36bb2ca2 554static CGColor Black_;
baf80942 555static CGColor Off_;
36bb2ca2 556static CGColor White_;
7b0ce2da 557static CGColor Gray_;
36bb2ca2 558
7d2ac47f 559static NSString *App_;
d73cede2
JF
560static NSString *Home_;
561static BOOL Sounds_Keyboard_;
562
2388b078 563static BOOL Advanced_;
c390d3ab 564#if !ForSaurik
6f1a15d9 565static BOOL Loaded_;
c390d3ab 566#endif
a54b1c10 567static BOOL Ignored_;
2388b078 568
f641a0e5
JF
569static UIFont *Font12_;
570static UIFont *Font12Bold_;
571static UIFont *Font14_;
572static UIFont *Font18Bold_;
573static UIFont *Font22Bold_;
574
87f46a96
JF
575static const char *Machine_ = NULL;
576static const NSString *UniqueID_ = NULL;
3178d79b 577
2a8d9add
JF
578unsigned Major_;
579unsigned Minor_;
580unsigned BugFix_;
581
199d0ba5 582CFLocaleRef Locale_;
36bb2ca2
JF
583CGColorSpaceRef space_;
584
2a8d9add 585#define FW_LEAST(major, minor, bugfix) \
51a1ffdf
JF
586 (major < Major_ || major == Major_ && \
587 (minor < Minor_ || minor == Minor_ && \
588 bugfix <= BugFix_))
2a8d9add 589
36bb2ca2 590bool bootstrap_;
77fcccaf 591bool reload_;
b31b87cc 592
46dbfd32 593static NSDictionary *SectionMap_;
b4d89997 594static NSMutableDictionary *Metadata_;
7b0ce2da
JF
595static _transient NSMutableDictionary *Settings_;
596static _transient NSString *Role_;
597static _transient NSMutableDictionary *Packages_;
598static _transient NSMutableDictionary *Sections_;
599static _transient NSMutableDictionary *Sources_;
bbb879fb 600static bool Changed_;
b4d89997 601static NSDate *now_;
8da60fb7 602
affeffc7
JF
603#if RecycleWebViews
604static NSMutableArray *Documents_;
605#endif
606
23672b39
JF
607NSString *GetLastUpdate() {
608 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
609
610 if (update == nil)
611 return @"Never or Unknown";
612
199d0ba5 613 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
23672b39
JF
614 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
615
616 CFRelease(formatter);
23672b39
JF
617
618 return [(NSString *) formatted autorelease];
619}
36bb2ca2
JF
620/* }}} */
621/* Display Helpers {{{ */
622inline float Interpolate(float begin, float end, float fraction) {
623 return (end - begin) * fraction + begin;
624}
2367a917 625
8fe19fc1 626NSString *SizeString(double size) {
affeffc7
JF
627 bool negative = size < 0;
628 if (negative)
629 size = -size;
630
8fe19fc1
JF
631 unsigned power = 0;
632 while (size > 1024) {
633 size /= 1024;
634 ++power;
635 }
636
637 static const char *powers_[] = {"B", "kB", "MB", "GB"};
638
86316a91 639 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
8fe19fc1
JF
640}
641
31f3cfff
JF
642NSString *StripVersion(NSString *version) {
643 NSRange colon = [version rangeOfString:@":"];
644 if (colon.location != NSNotFound)
645 version = [version substringFromIndex:(colon.location + 1)];
646 return version;
647}
648
4cf4165e
JF
649NSString *Simplify(NSString *title) {
650 const char *data = [title UTF8String];
651 size_t size = [title length];
652
7b0ce2da
JF
653 static Pcre square_r("^\\[(.*)\\]$");
654 if (square_r(data, size))
655 return Simplify(square_r[1]);
656
657 static Pcre paren_r("^\\((.*)\\)$");
658 if (paren_r(data, size))
659 return Simplify(paren_r[1]);
660
661 static Pcre title_r("^(.*?) \\(.*\\)$");
4cf4165e 662 if (title_r(data, size))
7b0ce2da
JF
663 return Simplify(title_r[1]);
664
665 return title;
4cf4165e 666}
36bb2ca2
JF
667/* }}} */
668
6d9712c4
JF
669bool isSectionVisible(NSString *section) {
670 NSDictionary *metadata = [Sections_ objectForKey:section];
671 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
672 return hidden == nil || ![hidden boolValue];
673}
674
d36e83a3 675/* Delegate Prototypes {{{ */
36bb2ca2
JF
676@class Package;
677@class Source;
678
31f3cfff
JF
679@interface NSObject (ProgressDelegate)
680@end
681
682@implementation NSObject(ProgressDelegate)
683
684- (void) _setProgressError:(NSArray *)args {
685 [self performSelector:@selector(setProgressError:forPackage:)
686 withObject:[args objectAtIndex:0]
687 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
688 ];
689}
690
691@end
692
36bb2ca2 693@protocol ProgressDelegate
31f3cfff 694- (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
36bb2ca2
JF
695- (void) setProgressTitle:(NSString *)title;
696- (void) setProgressPercent:(float)percent;
baf80942 697- (void) startProgress;
36bb2ca2 698- (void) addProgressOutput:(NSString *)output;
baf80942 699- (bool) isCancelling:(size_t)received;
36bb2ca2
JF
700@end
701
8b29f8e6 702@protocol ConfigurationDelegate
5a09ae08 703- (void) repairWithSelector:(SEL)selector;
8b29f8e6
JF
704- (void) setConfigurationData:(NSString *)data;
705@end
706
36bb2ca2
JF
707@protocol CydiaDelegate
708- (void) installPackage:(Package *)package;
709- (void) removePackage:(Package *)package;
a9543575 710- (void) slideUp:(UIActionSheet *)alert;
36bb2ca2 711- (void) distUpgrade;
6d9712c4 712- (void) updateData;
7b0ce2da
JF
713- (void) syncData;
714- (void) askForSettings;
715- (UIProgressHUD *) addProgressHUD;
c390d3ab
JF
716- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
717- (RVPage *) pageForPackage:(NSString *)name;
718- (void) openMailToURL:(NSURL *)url;
36bb2ca2 719@end
d36e83a3 720/* }}} */
b4d89997 721
dc5812ec
JF
722/* Status Delegation {{{ */
723class Status :
724 public pkgAcquireStatus
725{
726 private:
31f3cfff 727 _transient NSObject<ProgressDelegate> *delegate_;
dc5812ec
JF
728
729 public:
730 Status() :
731 delegate_(nil)
732 {
733 }
734
735 void setDelegate(id delegate) {
736 delegate_ = delegate;
737 }
738
739 virtual bool MediaChange(std::string media, std::string drive) {
740 return false;
741 }
742
743 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
dc5812ec
JF
744 }
745
746 virtual void Fetch(pkgAcquire::ItemDesc &item) {
2388b078 747 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
dc5812ec
JF
748 }
749
750 virtual void Done(pkgAcquire::ItemDesc &item) {
dc5812ec
JF
751 }
752
753 virtual void Fail(pkgAcquire::ItemDesc &item) {
1d80f6b9
JF
754 if (
755 item.Owner->Status == pkgAcquire::Item::StatIdle ||
756 item.Owner->Status == pkgAcquire::Item::StatDone
757 )
758 return;
759
affeffc7
JF
760 std::string &error(item.Owner->ErrorText);
761 if (error.empty())
762 return;
763
764 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
765 NSArray *fields([description componentsSeparatedByString:@" "]);
766 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
767
31f3cfff 768 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
affeffc7
JF
769 withObject:[NSArray arrayWithObjects:
770 [NSString stringWithUTF8String:error.c_str()],
771 source,
772 nil]
31f3cfff
JF
773 waitUntilDone:YES
774 ];
dc5812ec
JF
775 }
776
777 virtual bool Pulse(pkgAcquire *Owner) {
2367a917
JF
778 bool value = pkgAcquireStatus::Pulse(Owner);
779
780 float percent(
781 double(CurrentBytes + CurrentItems) /
782 double(TotalBytes + TotalItems)
783 );
784
36bb2ca2 785 [delegate_ setProgressPercent:percent];
baf80942 786 return [delegate_ isCancelling:CurrentBytes] ? false : value;
dc5812ec
JF
787 }
788
789 virtual void Start() {
baf80942 790 [delegate_ startProgress];
dc5812ec
JF
791 }
792
793 virtual void Stop() {
dc5812ec
JF
794 }
795};
796/* }}} */
797/* Progress Delegation {{{ */
798class Progress :
799 public OpProgress
800{
801 private:
36bb2ca2 802 _transient id<ProgressDelegate> delegate_;
dc5812ec
JF
803
804 protected:
805 virtual void Update() {
2388b078 806 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
36bb2ca2 807 [delegate_ setProgressPercent:(Percent / 100)];
dc5812ec
JF
808 }
809
810 public:
811 Progress() :
812 delegate_(nil)
813 {
814 }
815
816 void setDelegate(id delegate) {
817 delegate_ = delegate;
818 }
819
820 virtual void Done() {
36bb2ca2 821 [delegate_ setProgressPercent:1];
dc5812ec
JF
822 }
823};
824/* }}} */
825
36bb2ca2
JF
826/* Database Interface {{{ */
827@interface Database : NSObject {
828 pkgCacheFile cache_;
5a09ae08 829 pkgDepCache::Policy *policy_;
36bb2ca2
JF
830 pkgRecords *records_;
831 pkgProblemResolver *resolver_;
832 pkgAcquire *fetcher_;
833 FileFd *lock_;
834 SPtr<pkgPackageManager> manager_;
835 pkgSourceList *list_;
5f6bff8c 836
36bb2ca2
JF
837 NSMutableDictionary *sources_;
838 NSMutableArray *packages_;
b4d89997 839
31f3cfff 840 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
36bb2ca2
JF
841 Status status_;
842 Progress progress_;
8b29f8e6 843
77fcccaf 844 int cydiafd_;
36bb2ca2 845 int statusfd_;
8b29f8e6 846 FILE *input_;
dc5812ec
JF
847}
848
770f2a8e
JF
849+ (Database *) sharedInstance;
850
77fcccaf 851- (void) _readCydia:(NSNumber *)fd;
36bb2ca2
JF
852- (void) _readStatus:(NSNumber *)fd;
853- (void) _readOutput:(NSNumber *)fd;
2367a917 854
8b29f8e6
JF
855- (FILE *) input;
856
36bb2ca2 857- (Package *) packageWithName:(NSString *)name;
dc5812ec 858
36bb2ca2 859- (pkgCacheFile &) cache;
5a09ae08 860- (pkgDepCache::Policy *) policy;
36bb2ca2
JF
861- (pkgRecords *) records;
862- (pkgProblemResolver *) resolver;
863- (pkgAcquire &) fetcher;
864- (NSArray *) packages;
7b0ce2da 865- (NSArray *) sources;
36bb2ca2
JF
866- (void) reloadData;
867
5a09ae08 868- (void) configure;
36bb2ca2
JF
869- (void) prepare;
870- (void) perform;
871- (void) upgrade;
872- (void) update;
873
874- (void) updateWithStatus:(Status &)status;
875
876- (void) setDelegate:(id)delegate;
877- (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
dc5812ec 878@end
36bb2ca2 879/* }}} */
dc5812ec 880
36bb2ca2
JF
881/* Source Class {{{ */
882@interface Source : NSObject {
883 NSString *description_;
884 NSString *label_;
885 NSString *origin_;
dc5812ec 886
36bb2ca2
JF
887 NSString *uri_;
888 NSString *distribution_;
889 NSString *type_;
890 NSString *version_;
2367a917 891
36bb2ca2 892 NSString *defaultIcon_;
dc5812ec 893
7b0ce2da 894 NSDictionary *record_;
36bb2ca2 895 BOOL trusted_;
dc5812ec
JF
896}
897
36bb2ca2 898- (Source *) initWithMetaIndex:(metaIndex *)index;
dc5812ec 899
7b0ce2da
JF
900- (NSComparisonResult) compareByNameAndType:(Source *)source;
901
902- (NSDictionary *) record;
36bb2ca2 903- (BOOL) trusted;
dc5812ec 904
36bb2ca2
JF
905- (NSString *) uri;
906- (NSString *) distribution;
907- (NSString *) type;
7b0ce2da
JF
908- (NSString *) key;
909- (NSString *) host;
36bb2ca2 910
7b0ce2da 911- (NSString *) name;
36bb2ca2
JF
912- (NSString *) description;
913- (NSString *) label;
914- (NSString *) origin;
915- (NSString *) version;
dc5812ec 916
36bb2ca2 917- (NSString *) defaultIcon;
7b0ce2da 918
dc5812ec 919@end
dc5812ec 920
36bb2ca2 921@implementation Source
dc5812ec 922
36bb2ca2
JF
923- (void) dealloc {
924 [uri_ release];
925 [distribution_ release];
926 [type_ release];
dc5812ec 927
36bb2ca2
JF
928 if (description_ != nil)
929 [description_ release];
930 if (label_ != nil)
931 [label_ release];
932 if (origin_ != nil)
933 [origin_ release];
934 if (version_ != nil)
935 [version_ release];
936 if (defaultIcon_ != nil)
937 [defaultIcon_ release];
7b0ce2da
JF
938 if (record_ != nil)
939 [record_ release];
dc5812ec 940
36bb2ca2 941 [super dealloc];
dc5812ec
JF
942}
943
6f1a15d9
JF
944+ (NSArray *) _attributeKeys {
945 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
946}
947
948- (NSArray *) attributeKeys {
949 return [[self class] _attributeKeys];
950}
951
952+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
953 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
954}
955
36bb2ca2
JF
956- (Source *) initWithMetaIndex:(metaIndex *)index {
957 if ((self = [super init]) != nil) {
958 trusted_ = index->IsTrusted();
dc5812ec 959
2388b078
JF
960 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
961 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
962 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
dc5812ec 963
36bb2ca2
JF
964 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
965 if (dindex != NULL) {
966 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
967 std::string line;
968 while (std::getline(release, line)) {
969 std::string::size_type colon(line.find(':'));
970 if (colon == std::string::npos)
971 continue;
dc5812ec 972
36bb2ca2
JF
973 std::string name(line.substr(0, colon));
974 std::string value(line.substr(colon + 1));
975 while (!value.empty() && value[0] == ' ')
976 value = value.substr(1);
dc5812ec 977
36bb2ca2 978 if (name == "Default-Icon")
2388b078 979 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 980 else if (name == "Description")
2388b078 981 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 982 else if (name == "Label")
2388b078 983 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 984 else if (name == "Origin")
2388b078 985 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 986 else if (name == "Version")
2388b078 987 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2
JF
988 }
989 }
7b0ce2da
JF
990
991 record_ = [Sources_ objectForKey:[self key]];
992 if (record_ != nil)
993 record_ = [record_ retain];
36bb2ca2 994 } return self;
2367a917 995}
dc5812ec 996
7b0ce2da
JF
997- (NSComparisonResult) compareByNameAndType:(Source *)source {
998 NSDictionary *lhr = [self record];
999 NSDictionary *rhr = [source record];
1000
1001 if (lhr != rhr)
1002 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1003
1004 NSString *lhs = [self name];
1005 NSString *rhs = [source name];
1006
1007 if ([lhs length] != 0 && [rhs length] != 0) {
1008 unichar lhc = [lhs characterAtIndex:0];
1009 unichar rhc = [rhs characterAtIndex:0];
1010
1011 if (isalpha(lhc) && !isalpha(rhc))
1012 return NSOrderedAscending;
1013 else if (!isalpha(lhc) && isalpha(rhc))
1014 return NSOrderedDescending;
1015 }
1016
90515f88 1017 return [lhs compare:rhs options:CompareOptions_];
7b0ce2da
JF
1018}
1019
1020- (NSDictionary *) record {
1021 return record_;
1022}
1023
36bb2ca2
JF
1024- (BOOL) trusted {
1025 return trusted_;
1026}
3178d79b 1027
36bb2ca2
JF
1028- (NSString *) uri {
1029 return uri_;
1030}
ec97ef06 1031
36bb2ca2
JF
1032- (NSString *) distribution {
1033 return distribution_;
1034}
dc5812ec 1035
36bb2ca2
JF
1036- (NSString *) type {
1037 return type_;
dc5812ec
JF
1038}
1039
7b0ce2da
JF
1040- (NSString *) key {
1041 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1042}
1043
1044- (NSString *) host {
1045 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1046}
1047
1048- (NSString *) name {
1049 return origin_ == nil ? [self host] : origin_;
1050}
1051
36bb2ca2
JF
1052- (NSString *) description {
1053 return description_;
1054}
b4d89997 1055
36bb2ca2 1056- (NSString *) label {
7b0ce2da 1057 return label_ == nil ? [self host] : label_;
36bb2ca2 1058}
3178d79b 1059
36bb2ca2
JF
1060- (NSString *) origin {
1061 return origin_;
1062}
3178d79b 1063
36bb2ca2
JF
1064- (NSString *) version {
1065 return version_;
1066}
2367a917 1067
36bb2ca2
JF
1068- (NSString *) defaultIcon {
1069 return defaultIcon_;
1070}
2367a917 1071
2388b078
JF
1072@end
1073/* }}} */
1074/* Relationship Class {{{ */
1075@interface Relationship : NSObject {
1076 NSString *type_;
1077 NSString *id_;
1078}
1079
1080- (NSString *) type;
1081- (NSString *) id;
1082- (NSString *) name;
1083
1084@end
1085
1086@implementation Relationship
1087
1088- (void) dealloc {
1089 [type_ release];
1090 [id_ release];
1091 [super dealloc];
1092}
1093
1094- (NSString *) type {
1095 return type_;
1096}
1097
1098- (NSString *) id {
1099 return id_;
1100}
1101
1102- (NSString *) name {
1103 _assert(false);
1104 return nil;
1105}
1106
dc5812ec 1107@end
b4d89997 1108/* }}} */
36bb2ca2
JF
1109/* Package Class {{{ */
1110NSString *Scour(const char *field, const char *begin, const char *end) {
1111 size_t i(0), l(strlen(field));
dc5812ec 1112
36bb2ca2
JF
1113 for (;;) {
1114 const char *name = begin + i;
1115 const char *colon = name + l;
1116 const char *value = colon + 1;
06aa974d 1117
36bb2ca2
JF
1118 if (
1119 value < end &&
1120 *colon == ':' &&
affeffc7 1121 strncasecmp(name, field, l) == 0
36bb2ca2
JF
1122 ) {
1123 while (value != end && value[0] == ' ')
1124 ++value;
1125 const char *line = std::find(value, end, '\n');
1126 while (line != value && line[-1] == ' ')
1127 --line;
2388b078
JF
1128
1129 return [NSString stringWithUTF8Bytes:value length:(line - value)];
36bb2ca2
JF
1130 } else {
1131 begin = std::find(begin, end, '\n');
1132 if (begin == end)
1133 return nil;
1134 ++begin;
1135 }
1136 }
1137}
b4d89997 1138
36bb2ca2
JF
1139@interface Package : NSObject {
1140 pkgCache::PkgIterator iterator_;
1141 _transient Database *database_;
1142 pkgCache::VerIterator version_;
1143 pkgCache::VerFileIterator file_;
bbb879fb 1144
36bb2ca2 1145 Source *source_;
bbb879fb 1146 bool cached_;
dc5812ec 1147
807ae6d7
JF
1148 NSString *section_;
1149
36bb2ca2
JF
1150 NSString *latest_;
1151 NSString *installed_;
dc5812ec 1152
36bb2ca2
JF
1153 NSString *id_;
1154 NSString *name_;
1155 NSString *tagline_;
1156 NSString *icon_;
25a2158d 1157 NSString *depiction_;
a9a0661e 1158 NSString *homepage_;
795d26fc 1159 Address *sponsor_;
77fcccaf 1160 Address *author_;
6d9712c4 1161 NSArray *tags_;
7b0ce2da 1162 NSString *role_;
2388b078
JF
1163
1164 NSArray *relationships_;
b4d89997
JF
1165}
1166
5a09ae08 1167- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
36bb2ca2 1168+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
b4d89997 1169
2388b078
JF
1170- (pkgCache::PkgIterator) iterator;
1171
36bb2ca2
JF
1172- (NSString *) section;
1173- (Address *) maintainer;
1174- (size_t) size;
1175- (NSString *) description;
1176- (NSString *) index;
dc5812ec 1177
807ae6d7 1178- (NSMutableDictionary *) metadata;
36bb2ca2 1179- (NSDate *) seen;
807ae6d7
JF
1180- (BOOL) subscribed;
1181- (BOOL) ignored;
9e98e020 1182
36bb2ca2
JF
1183- (NSString *) latest;
1184- (NSString *) installed;
5a09ae08
JF
1185
1186- (BOOL) valid;
31f3cfff 1187- (BOOL) upgradableAndEssential:(BOOL)essential;
36bb2ca2
JF
1188- (BOOL) essential;
1189- (BOOL) broken;
7d2ac47f 1190- (BOOL) unfiltered;
6d9712c4 1191- (BOOL) visible;
b4d89997 1192
9bedffaa
JF
1193- (BOOL) half;
1194- (BOOL) halfConfigured;
1195- (BOOL) halfInstalled;
1196- (BOOL) hasMode;
1197- (NSString *) mode;
1198
36bb2ca2
JF
1199- (NSString *) id;
1200- (NSString *) name;
1201- (NSString *) tagline;
770f2a8e 1202- (UIImage *) icon;
6f1a15d9 1203- (NSString *) homepage;
25a2158d 1204- (NSString *) depiction;
77fcccaf 1205- (Address *) author;
b4d89997 1206
affeffc7 1207- (NSArray *) files;
2388b078 1208- (NSArray *) relationships;
affeffc7
JF
1209- (NSArray *) warnings;
1210- (NSArray *) applications;
2388b078 1211
36bb2ca2 1212- (Source *) source;
7b0ce2da 1213- (NSString *) role;
b4d89997 1214
36bb2ca2 1215- (BOOL) matches:(NSString *)text;
b4d89997 1216
7b0ce2da 1217- (bool) hasSupportingRole;
6d9712c4 1218- (BOOL) hasTag:(NSString *)tag;
c390d3ab 1219- (NSString *) primaryPurpose;
770f2a8e 1220- (NSArray *) purposes;
6d9712c4 1221
36bb2ca2
JF
1222- (NSComparisonResult) compareByName:(Package *)package;
1223- (NSComparisonResult) compareBySection:(Package *)package;
807ae6d7
JF
1224
1225- (uint32_t) compareForChanges;
b4d89997 1226
36bb2ca2
JF
1227- (void) install;
1228- (void) remove;
06aa974d 1229
7d2ac47f 1230- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
440c215d 1231- (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
7b0ce2da
JF
1232- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1233- (NSNumber *) isVisibleInSource:(Source *)source;
b4d89997 1234
36bb2ca2 1235@end
b4d89997 1236
36bb2ca2 1237@implementation Package
b4d89997 1238
36bb2ca2 1239- (void) dealloc {
bbb879fb
JF
1240 if (source_ != nil)
1241 [source_ release];
1242
807ae6d7
JF
1243 if (section_ != nil)
1244 [section_ release];
1245
36bb2ca2
JF
1246 [latest_ release];
1247 if (installed_ != nil)
1248 [installed_ release];
b4d89997 1249
36bb2ca2
JF
1250 [id_ release];
1251 if (name_ != nil)
1252 [name_ release];
1253 [tagline_ release];
1254 if (icon_ != nil)
1255 [icon_ release];
25a2158d
JF
1256 if (depiction_ != nil)
1257 [depiction_ release];
a9a0661e
JF
1258 if (homepage_ != nil)
1259 [homepage_ release];
795d26fc
JF
1260 if (sponsor_ != nil)
1261 [sponsor_ release];
77fcccaf
JF
1262 if (author_ != nil)
1263 [author_ release];
6d9712c4
JF
1264 if (tags_ != nil)
1265 [tags_ release];
7b0ce2da
JF
1266 if (role_ != nil)
1267 [role_ release];
9e98e020 1268
2388b078
JF
1269 if (relationships_ != nil)
1270 [relationships_ release];
1271
36bb2ca2 1272 [super dealloc];
b4d89997
JF
1273}
1274
6f1a15d9 1275+ (NSArray *) _attributeKeys {
770f2a8e 1276 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
6f1a15d9
JF
1277}
1278
1279- (NSArray *) attributeKeys {
1280 return [[self class] _attributeKeys];
1281}
1282
1283+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1284 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1285}
1286
5a09ae08 1287- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
36bb2ca2
JF
1288 if ((self = [super init]) != nil) {
1289 iterator_ = iterator;
1290 database_ = database;
b4d89997 1291
5a09ae08 1292 version_ = [database_ policy]->GetCandidateVer(iterator_);
a4b0ec52
JF
1293 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
1294 latest_ = [StripVersion(latest) retain];
1295
1296 pkgCache::VerIterator current = iterator_.CurrentVer();
1297 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1298 installed_ = [StripVersion(installed) retain];
bbb879fb 1299
5a09ae08
JF
1300 if (!version_.end())
1301 file_ = version_.FileList();
1302 else {
1303 pkgCache &cache([database_ cache]);
1304 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1305 }
b4d89997 1306
86316a91 1307 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
5a09ae08
JF
1308
1309 if (!file_.end()) {
1310 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1311
1312 const char *begin, *end;
1313 parser->GetRec(begin, end);
1314
affeffc7 1315 name_ = Scour("name", begin, end);
5a09ae08
JF
1316 if (name_ != nil)
1317 name_ = [name_ retain];
1318 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
affeffc7 1319 icon_ = Scour("icon", begin, end);
5a09ae08
JF
1320 if (icon_ != nil)
1321 icon_ = [icon_ retain];
affeffc7 1322 depiction_ = Scour("depiction", begin, end);
a9a0661e
JF
1323 if (depiction_ != nil)
1324 depiction_ = [depiction_ retain];
affeffc7 1325 homepage_ = Scour("homepage", begin, end);
6f1a15d9 1326 if (homepage_ == nil)
affeffc7 1327 homepage_ = Scour("website", begin, end);
a9a0661e
JF
1328 if ([homepage_ isEqualToString:depiction_])
1329 homepage_ = nil;
6f1a15d9
JF
1330 if (homepage_ != nil)
1331 homepage_ = [homepage_ retain];
affeffc7 1332 NSString *sponsor = Scour("sponsor", begin, end);
795d26fc
JF
1333 if (sponsor != nil)
1334 sponsor_ = [[Address addressWithString:sponsor] retain];
affeffc7 1335 NSString *author = Scour("author", begin, end);
77fcccaf 1336 if (author != nil)
795d26fc 1337 author_ = [[Address addressWithString:author] retain];
affeffc7 1338 NSString *tags = Scour("tag", begin, end);
6d9712c4
JF
1339 if (tags != nil)
1340 tags_ = [[tags componentsSeparatedByString:@", "] retain];
5a09ae08 1341 }
06aa974d 1342
7b0ce2da
JF
1343 if (tags_ != nil)
1344 for (int i(0), e([tags_ count]); i != e; ++i) {
1345 NSString *tag = [tags_ objectAtIndex:i];
1346 if ([tag hasPrefix:@"role::"]) {
1347 role_ = [[tag substringFromIndex:6] retain];
1348 break;
1349 }
1350 }
1351
a4b0ec52
JF
1352 NSString *solid(latest == nil ? installed : latest);
1353 bool changed(false);
1354
86316a91
JF
1355 NSString *key([id_ lowercaseString]);
1356
1357 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
a4b0ec52 1358 if (metadata == nil) {
807ae6d7 1359 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
36bb2ca2 1360 now_, @"FirstSeen",
807ae6d7 1361 nil] mutableCopy];
b4d89997 1362
a4b0ec52
JF
1363 if (solid != nil)
1364 [metadata setObject:solid forKey:@"LastVersion"];
1365 changed = true;
1366 } else {
1367 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1368 NSDate *last([metadata objectForKey:@"LastSeen"]);
1369 NSString *version([metadata objectForKey:@"LastVersion"]);
1370
1371 if (first == nil) {
1372 first = last == nil ? now_ : last;
1373 [metadata setObject:first forKey:@"FirstSeen"];
1374 changed = true;
1375 }
1376
1377 if (solid != nil)
1378 if (version == nil) {
1379 [metadata setObject:solid forKey:@"LastVersion"];
1380 changed = true;
1381 } else if (![version isEqualToString:solid]) {
1382 [metadata setObject:solid forKey:@"LastVersion"];
1383 last = now_;
1384 [metadata setObject:last forKey:@"LastSeen"];
1385 changed = true;
1386 }
1387 }
1388
1389 if (changed) {
86316a91 1390 [Packages_ setObject:metadata forKey:key];
bbb879fb 1391 Changed_ = true;
36bb2ca2
JF
1392 }
1393 } return self;
dc5812ec
JF
1394}
1395
36bb2ca2 1396+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
5a09ae08
JF
1397 return [[[Package alloc]
1398 initWithIterator:iterator
1399 database:database
1400 ] autorelease];
dc5812ec
JF
1401}
1402
2388b078
JF
1403- (pkgCache::PkgIterator) iterator {
1404 return iterator_;
1405}
1406
36bb2ca2 1407- (NSString *) section {
807ae6d7
JF
1408 if (section_ != nil)
1409 return section_;
1410
36bb2ca2 1411 const char *section = iterator_.Section();
7b0ce2da
JF
1412 if (section == NULL)
1413 return nil;
46dbfd32
JF
1414
1415 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1416
1417 lookup:
1418 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1419 if (NSString *rename = [value objectForKey:@"Rename"]) {
1420 name = rename;
1421 goto lookup;
1422 }
1423
807ae6d7
JF
1424 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1425 return section_;
dc5812ec
JF
1426}
1427
36bb2ca2 1428- (Address *) maintainer {
5a09ae08
JF
1429 if (file_.end())
1430 return nil;
36bb2ca2 1431 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
2388b078 1432 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
8fe19fc1
JF
1433}
1434
36bb2ca2 1435- (size_t) size {
5a09ae08 1436 return version_.end() ? 0 : version_->InstalledSize;
dc5812ec
JF
1437}
1438
36bb2ca2 1439- (NSString *) description {
5a09ae08
JF
1440 if (file_.end())
1441 return nil;
36bb2ca2 1442 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
2388b078 1443 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
8fe19fc1 1444
36bb2ca2
JF
1445 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1446 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1447 if ([lines count] < 2)
1448 return nil;
3178d79b 1449
36bb2ca2
JF
1450 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1451 for (size_t i(1); i != [lines count]; ++i) {
1452 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1453 [trimmed addObject:trim];
1454 }
3178d79b 1455
36bb2ca2
JF
1456 return [trimmed componentsJoinedByString:@"\n"];
1457}
dc5812ec 1458
36bb2ca2
JF
1459- (NSString *) index {
1460 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1461 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1462}
3178d79b 1463
807ae6d7
JF
1464- (NSMutableDictionary *) metadata {
1465 return [Packages_ objectForKey:[id_ lowercaseString]];
1466}
1467
36bb2ca2 1468- (NSDate *) seen {
807ae6d7
JF
1469 NSDictionary *metadata([self metadata]);
1470 if ([self subscribed])
a4b0ec52
JF
1471 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1472 return last;
1473 return [metadata objectForKey:@"FirstSeen"];
3178d79b
JF
1474}
1475
807ae6d7
JF
1476- (BOOL) subscribed {
1477 NSDictionary *metadata([self metadata]);
1478 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1479 return [subscribed boolValue];
1480 else
1481 return false;
1482}
1483
1484- (BOOL) ignored {
1485 NSDictionary *metadata([self metadata]);
1486 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1487 return [ignored boolValue];
1488 else
1489 return false;
1490}
1491
36bb2ca2
JF
1492- (NSString *) latest {
1493 return latest_;
8fe19fc1
JF
1494}
1495
36bb2ca2
JF
1496- (NSString *) installed {
1497 return installed_;
3178d79b
JF
1498}
1499
5a09ae08
JF
1500- (BOOL) valid {
1501 return !version_.end();
1502}
1503
31f3cfff 1504- (BOOL) upgradableAndEssential:(BOOL)essential {
a54b1c10
JF
1505 pkgCache::VerIterator current = iterator_.CurrentVer();
1506
1507 if (current.end())
31f3cfff 1508 return essential && [self essential];
a54b1c10 1509 else {
5a09ae08 1510 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
a54b1c10
JF
1511 return !candidate.end() && candidate != current;
1512 }
36bb2ca2 1513}
3178d79b 1514
36bb2ca2
JF
1515- (BOOL) essential {
1516 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
3178d79b
JF
1517}
1518
36bb2ca2 1519- (BOOL) broken {
9bedffaa
JF
1520 return [database_ cache][iterator_].InstBroken();
1521}
1522
7d2ac47f 1523- (BOOL) unfiltered {
6d9712c4 1524 NSString *section = [self section];
7d2ac47f
JF
1525 return section == nil || isSectionVisible(section);
1526}
1527
1528- (BOOL) visible {
1529 return [self hasSupportingRole] && [self unfiltered];
6d9712c4
JF
1530}
1531
9bedffaa
JF
1532- (BOOL) half {
1533 unsigned char current = iterator_->CurrentState;
1534 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1535}
1536
1537- (BOOL) halfConfigured {
1538 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1539}
1540
1541- (BOOL) halfInstalled {
1542 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1543}
1544
1545- (BOOL) hasMode {
1546 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1547 return state.Mode != pkgDepCache::ModeKeep;
1548}
1549
1550- (NSString *) mode {
1551 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1552
1553 switch (state.Mode) {
1554 case pkgDepCache::ModeDelete:
1555 if ((state.iFlags & pkgDepCache::Purge) != 0)
1556 return @"Purge";
1557 else
1558 return @"Remove";
9bedffaa
JF
1559 case pkgDepCache::ModeKeep:
1560 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1561 return nil;
1562 else
1563 return nil;
9bedffaa
JF
1564 case pkgDepCache::ModeInstall:
1565 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1566 return @"Reinstall";
1567 else switch (state.Status) {
1568 case -1:
1569 return @"Downgrade";
1570 case 0:
1571 return @"Install";
1572 case 1:
1573 return @"Upgrade";
1574 case 2:
1575 return @"New Install";
1576 default:
1577 _assert(false);
1578 }
1579 default:
1580 _assert(false);
1581 }
8fe19fc1
JF
1582}
1583
36bb2ca2
JF
1584- (NSString *) id {
1585 return id_;
8fe19fc1
JF
1586}
1587
36bb2ca2
JF
1588- (NSString *) name {
1589 return name_ == nil ? id_ : name_;
1590}
8fe19fc1 1591
36bb2ca2
JF
1592- (NSString *) tagline {
1593 return tagline_;
8fe19fc1
JF
1594}
1595
770f2a8e
JF
1596- (UIImage *) icon {
1597 NSString *section = [self section];
1598 if (section != nil)
1599 section = Simplify(section);
1600
1601 UIImage *icon(nil);
1602 if (NSString *icon = icon_)
1603 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1604 if (icon == nil) if (section != nil)
1605 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1606 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1607 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1608 if (icon == nil)
1609 icon = [UIImage applicationImageNamed:@"unknown.png"];
1610 return icon;
36bb2ca2 1611}
8fe19fc1 1612
6f1a15d9 1613- (NSString *) homepage {
3a49daf0 1614 return homepage_;
8fe19fc1
JF
1615}
1616
25a2158d
JF
1617- (NSString *) depiction {
1618 return depiction_;
1619}
1620
795d26fc
JF
1621- (Address *) sponsor {
1622 return sponsor_;
1623}
1624
77fcccaf
JF
1625- (Address *) author {
1626 return author_;
1627}
1628
affeffc7
JF
1629- (NSArray *) files {
1630 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1631 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1632
1633 std::ifstream fin;
1634 fin.open([path UTF8String]);
1635 if (!fin.is_open())
1636 return nil;
1637
1638 std::string line;
1639 while (std::getline(fin, line))
1640 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1641
1642 return files;
1643}
1644
2388b078
JF
1645- (NSArray *) relationships {
1646 return relationships_;
1647}
1648
affeffc7
JF
1649- (NSArray *) warnings {
1650 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1651 const char *name(iterator_.Name());
1652
1653 size_t length(strlen(name));
1654 if (length < 2) invalid:
1655 [warnings addObject:@"illegal package identifier"];
1656 else for (size_t i(0); i != length; ++i)
1657 if (
1658 (name[i] < 'a' || name[i] > 'z') &&
1659 (name[i] < '0' || name[i] > '9') &&
1660 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1661 ) goto invalid;
1662
1663 if (strcmp(name, "cydia") != 0) {
1664 bool cydia = false;
1665 bool stash = false;
1666
1667 if (NSArray *files = [self files])
1668 for (NSString *file in files)
1669 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1670 cydia = true;
1671 else if (!stash && [file isEqualToString:@"/var/stash"])
1672 stash = true;
1673
1674 if (cydia)
1675 [warnings addObject:@"files installed into Cydia.app"];
1676 if (stash)
1677 [warnings addObject:@"files installed to /var/stash"];
1678 }
1679
1680 return [warnings count] == 0 ? nil : warnings;
1681}
1682
1683- (NSArray *) applications {
1684 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1685
1686 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1687
1688 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1689 if (NSArray *files = [self files])
1690 for (NSString *file in files)
1691 if (application_r(file)) {
1692 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1693 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1694 if ([id isEqualToString:me])
1695 continue;
1696
1697 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1698 if (display == nil)
1699 display = application_r[1];
1700
1701 NSString *bundle([file stringByDeletingLastPathComponent]);
1702 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1703 if (icon == nil || [icon length] == 0)
1704 icon = @"icon.png";
1705 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1706
1707 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1708 [applications addObject:application];
1709
1710 [application addObject:id];
1711 [application addObject:display];
1712 [application addObject:url];
1713 }
1714
1715 return [applications count] == 0 ? nil : applications;
1716}
1717
36bb2ca2 1718- (Source *) source {
bbb879fb 1719 if (!cached_) {
5a09ae08 1720 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
bbb879fb
JF
1721 cached_ = true;
1722 }
1723
36bb2ca2 1724 return source_;
8fe19fc1
JF
1725}
1726
7b0ce2da
JF
1727- (NSString *) role {
1728 return role_;
1729}
1730
36bb2ca2
JF
1731- (BOOL) matches:(NSString *)text {
1732 if (text == nil)
1733 return NO;
8fe19fc1 1734
36bb2ca2 1735 NSRange range;
8fe19fc1 1736
baf80942 1737 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
36bb2ca2
JF
1738 if (range.location != NSNotFound)
1739 return YES;
8fe19fc1 1740
36bb2ca2
JF
1741 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1742 if (range.location != NSNotFound)
1743 return YES;
8fe19fc1 1744
36bb2ca2
JF
1745 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1746 if (range.location != NSNotFound)
1747 return YES;
8fe19fc1 1748
36bb2ca2
JF
1749 return NO;
1750}
8fe19fc1 1751
7b0ce2da
JF
1752- (bool) hasSupportingRole {
1753 if (role_ == nil)
6d9712c4 1754 return true;
7b0ce2da
JF
1755 if ([role_ isEqualToString:@"enduser"])
1756 return true;
1757 if ([Role_ isEqualToString:@"User"])
1758 return false;
1759 if ([role_ isEqualToString:@"hacker"])
1760 return true;
1761 if ([Role_ isEqualToString:@"Hacker"])
1762 return false;
1763 if ([role_ isEqualToString:@"developer"])
1764 return true;
1765 if ([Role_ isEqualToString:@"Developer"])
1766 return false;
1767 _assert(false);
6d9712c4
JF
1768}
1769
1770- (BOOL) hasTag:(NSString *)tag {
1771 return tags_ == nil ? NO : [tags_ containsObject:tag];
1772}
1773
c390d3ab
JF
1774- (NSString *) primaryPurpose {
1775 for (NSString *tag in tags_)
1776 if ([tag hasPrefix:@"purpose::"])
1777 return [tag substringFromIndex:9];
1778 return nil;
1779}
1780
770f2a8e
JF
1781- (NSArray *) purposes {
1782 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1783 for (NSString *tag in tags_)
1784 if ([tag hasPrefix:@"purpose::"])
1785 [purposes addObject:[tag substringFromIndex:9]];
1786 return [purposes count] == 0 ? nil : purposes;
1787}
1788
36bb2ca2
JF
1789- (NSComparisonResult) compareByName:(Package *)package {
1790 NSString *lhs = [self name];
1791 NSString *rhs = [package name];
8fe19fc1 1792
36bb2ca2
JF
1793 if ([lhs length] != 0 && [rhs length] != 0) {
1794 unichar lhc = [lhs characterAtIndex:0];
1795 unichar rhc = [rhs characterAtIndex:0];
1796
1797 if (isalpha(lhc) && !isalpha(rhc))
1798 return NSOrderedAscending;
1799 else if (!isalpha(lhc) && isalpha(rhc))
1800 return NSOrderedDescending;
8fe19fc1
JF
1801 }
1802
90515f88 1803 return [lhs compare:rhs options:CompareOptions_];
8fe19fc1
JF
1804}
1805
36bb2ca2
JF
1806- (NSComparisonResult) compareBySection:(Package *)package {
1807 NSString *lhs = [self section];
1808 NSString *rhs = [package section];
3178d79b 1809
36bb2ca2
JF
1810 if (lhs == NULL && rhs != NULL)
1811 return NSOrderedAscending;
1812 else if (lhs != NULL && rhs == NULL)
1813 return NSOrderedDescending;
1814 else if (lhs != NULL && rhs != NULL) {
90515f88 1815 NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
36bb2ca2
JF
1816 if (result != NSOrderedSame)
1817 return result;
1818 }
3178d79b 1819
36bb2ca2
JF
1820 return NSOrderedSame;
1821}
3178d79b 1822
807ae6d7
JF
1823- (uint32_t) compareForChanges {
1824 union {
1825 uint32_t key;
3178d79b 1826
807ae6d7
JF
1827 struct {
1828 uint32_t timestamp : 30;
1829 uint32_t ignored : 1;
1830 uint32_t upgradable : 1;
1831 } bits;
1832 } value;
3178d79b 1833
807ae6d7 1834 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
8fe19fc1 1835
807ae6d7
JF
1836 if ([self upgradableAndEssential:YES]) {
1837 value.bits.timestamp = 0;
1838 value.bits.ignored = [self ignored] ? 0 : 1;
1839 value.bits.upgradable = 1;
1840 } else {
1841 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1842 value.bits.ignored = 0;
1843 value.bits.upgradable = 0;
36bb2ca2 1844 }
8fe19fc1 1845
807ae6d7 1846 return _not(uint32_t) - value.key;
36bb2ca2 1847}
8fe19fc1 1848
36bb2ca2
JF
1849- (void) install {
1850 pkgProblemResolver *resolver = [database_ resolver];
1851 resolver->Clear(iterator_);
1852 resolver->Protect(iterator_);
1853 pkgCacheFile &cache([database_ cache]);
1854 cache->MarkInstall(iterator_, false);
1855 pkgDepCache::StateCache &state((*cache)[iterator_]);
1856 if (!state.Install())
1857 cache->SetReInstall(iterator_, true);
1858}
68a238ec 1859
36bb2ca2
JF
1860- (void) remove {
1861 pkgProblemResolver *resolver = [database_ resolver];
1862 resolver->Clear(iterator_);
1863 resolver->Protect(iterator_);
1864 resolver->Remove(iterator_);
1865 [database_ cache]->MarkDelete(iterator_, true);
1866}
8fe19fc1 1867
7d2ac47f 1868- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
f641a0e5 1869 return [NSNumber numberWithBool:(
7d2ac47f 1870 [self unfiltered] && [self matches:search]
f641a0e5 1871 )];
36bb2ca2 1872}
8fe19fc1 1873
440c215d 1874- (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
f641a0e5 1875 return [NSNumber numberWithBool:(
440c215d 1876 (![number boolValue] || [self visible]) && [self installed] != nil
f641a0e5 1877 )];
36bb2ca2 1878}
8fe19fc1 1879
7b0ce2da 1880- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
5a09ae08
JF
1881 NSString *section = [self section];
1882
7b0ce2da 1883 return [NSNumber numberWithBool:(
7d2ac47f 1884 [self visible] &&
7b0ce2da
JF
1885 [self installed] == nil && (
1886 name == nil ||
1887 section == nil && [name length] == 0 ||
1888 [name isEqualToString:section]
1889 )
1890 )];
1891}
1892
1893- (NSNumber *) isVisibleInSource:(Source *)source {
1894 return [NSNumber numberWithBool:([self source] == source && [self visible])];
36bb2ca2 1895}
8fe19fc1 1896
36bb2ca2
JF
1897@end
1898/* }}} */
1899/* Section Class {{{ */
1900@interface Section : NSObject {
1901 NSString *name_;
1902 size_t row_;
1903 size_t count_;
1904}
3178d79b 1905
6d9712c4
JF
1906- (NSComparisonResult) compareByName:(Section *)section;
1907- (Section *) initWithName:(NSString *)name;
36bb2ca2
JF
1908- (Section *) initWithName:(NSString *)name row:(size_t)row;
1909- (NSString *) name;
1910- (size_t) row;
1911- (size_t) count;
1912- (void) addToCount;
b19871dd 1913
36bb2ca2 1914@end
b19871dd 1915
36bb2ca2 1916@implementation Section
b19871dd 1917
36bb2ca2
JF
1918- (void) dealloc {
1919 [name_ release];
1920 [super dealloc];
1921}
b19871dd 1922
6d9712c4
JF
1923- (NSComparisonResult) compareByName:(Section *)section {
1924 NSString *lhs = [self name];
1925 NSString *rhs = [section name];
1926
1927 if ([lhs length] != 0 && [rhs length] != 0) {
1928 unichar lhc = [lhs characterAtIndex:0];
1929 unichar rhc = [rhs characterAtIndex:0];
1930
1931 if (isalpha(lhc) && !isalpha(rhc))
1932 return NSOrderedAscending;
1933 else if (!isalpha(lhc) && isalpha(rhc))
1934 return NSOrderedDescending;
1935 }
1936
90515f88 1937 return [lhs compare:rhs options:CompareOptions_];
6d9712c4
JF
1938}
1939
1940- (Section *) initWithName:(NSString *)name {
1941 return [self initWithName:name row:0];
1942}
1943
36bb2ca2
JF
1944- (Section *) initWithName:(NSString *)name row:(size_t)row {
1945 if ((self = [super init]) != nil) {
1946 name_ = [name retain];
1947 row_ = row;
b19871dd
JF
1948 } return self;
1949}
1950
36bb2ca2
JF
1951- (NSString *) name {
1952 return name_;
1953}
1954
1955- (size_t) row {
1956 return row_;
1957}
1958
1959- (size_t) count {
1960 return count_;
1961}
1962
1963- (void) addToCount {
1964 ++count_;
1965}
1966
b19871dd
JF
1967@end
1968/* }}} */
1969
affeffc7
JF
1970static int Finish_;
1971static NSArray *Finishes_;
bc8cd583 1972
36bb2ca2
JF
1973/* Database Implementation {{{ */
1974@implementation Database
ec97ef06 1975
770f2a8e
JF
1976+ (Database *) sharedInstance {
1977 static Database *instance;
1978 if (instance == nil)
1979 instance = [[Database alloc] init];
1980 return instance;
1981}
1982
36bb2ca2
JF
1983- (void) dealloc {
1984 _assert(false);
1985 [super dealloc];
1986}
ec97ef06 1987
affeffc7 1988- (void) _readCydia:(NSNumber *)fd { _pooled
77fcccaf
JF
1989 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1990 std::istream is(&ib);
1991 std::string line;
1992
bc8cd583
JF
1993 static Pcre finish_r("^finish:([^:]*)$");
1994
77fcccaf
JF
1995 while (std::getline(is, line)) {
1996 const char *data(line.c_str());
bc8cd583 1997 size_t size = line.size();
c390d3ab 1998 lprintf("C:%s\n", data);
bc8cd583
JF
1999
2000 if (finish_r(data, size)) {
2001 NSString *finish = finish_r[1];
2002 int index = [Finishes_ indexOfObject:finish];
2003 if (index != INT_MAX && index > Finish_)
2004 Finish_ = index;
2005 }
77fcccaf
JF
2006 }
2007
77fcccaf
JF
2008 _assert(false);
2009}
2010
affeffc7 2011- (void) _readStatus:(NSNumber *)fd { _pooled
36bb2ca2
JF
2012 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2013 std::istream is(&ib);
2014 std::string line;
ec97ef06 2015
7b0ce2da
JF
2016 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2017 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
ec97ef06 2018
36bb2ca2
JF
2019 while (std::getline(is, line)) {
2020 const char *data(line.c_str());
5a09ae08 2021 size_t size = line.size();
c390d3ab 2022 lprintf("S:%s\n", data);
ec97ef06 2023
5a09ae08
JF
2024 if (conffile_r(data, size)) {
2025 [delegate_ setConfigurationData:conffile_r[1]];
2026 } else if (strncmp(data, "status: ", 8) == 0) {
2027 NSString *string = [NSString stringWithUTF8String:(data + 8)];
36bb2ca2 2028 [delegate_ setProgressTitle:string];
5a09ae08 2029 } else if (pmstatus_r(data, size)) {
31f3cfff
JF
2030 std::string type([pmstatus_r[1] UTF8String]);
2031 NSString *id = pmstatus_r[2];
2032
5a09ae08
JF
2033 float percent([pmstatus_r[3] floatValue]);
2034 [delegate_ setProgressPercent:(percent / 100)];
2035
2036 NSString *string = pmstatus_r[4];
5a09ae08
JF
2037
2038 if (type == "pmerror")
31f3cfff
JF
2039 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2040 withObject:[NSArray arrayWithObjects:string, id, nil]
2041 waitUntilDone:YES
2042 ];
5a09ae08
JF
2043 else if (type == "pmstatus")
2044 [delegate_ setProgressTitle:string];
2045 else if (type == "pmconffile")
2046 [delegate_ setConfigurationData:string];
2047 else _assert(false);
2048 } else _assert(false);
36bb2ca2 2049 }
ec97ef06 2050
36bb2ca2
JF
2051 _assert(false);
2052}
ec97ef06 2053
affeffc7 2054- (void) _readOutput:(NSNumber *)fd { _pooled
36bb2ca2
JF
2055 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2056 std::istream is(&ib);
2057 std::string line;
2058
5a09ae08 2059 while (std::getline(is, line)) {
c390d3ab 2060 lprintf("O:%s\n", line.c_str());
2388b078 2061 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
5a09ae08 2062 }
36bb2ca2 2063
36bb2ca2 2064 _assert(false);
ec97ef06
JF
2065}
2066
8b29f8e6
JF
2067- (FILE *) input {
2068 return input_;
2069}
2070
36bb2ca2 2071- (Package *) packageWithName:(NSString *)name {
5a09ae08
JF
2072 if (static_cast<pkgDepCache *>(cache_) == NULL)
2073 return nil;
36bb2ca2
JF
2074 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2075 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2076}
2077
2078- (Database *) init {
ec97ef06 2079 if ((self = [super init]) != nil) {
5a09ae08 2080 policy_ = NULL;
36bb2ca2
JF
2081 records_ = NULL;
2082 resolver_ = NULL;
2083 fetcher_ = NULL;
2084 lock_ = NULL;
ec97ef06 2085
36bb2ca2
JF
2086 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2087 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
ec97ef06 2088
36bb2ca2 2089 int fds[2];
ec97ef06 2090
77fcccaf
JF
2091 _assert(pipe(fds) != -1);
2092 cydiafd_ = fds[1];
2093
2094 _config->Set("APT::Keep-Fds::", cydiafd_);
bc8cd583 2095 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
77fcccaf
JF
2096
2097 [NSThread
2098 detachNewThreadSelector:@selector(_readCydia:)
2099 toTarget:self
2100 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2101 ];
2102
36bb2ca2
JF
2103 _assert(pipe(fds) != -1);
2104 statusfd_ = fds[1];
ec97ef06 2105
36bb2ca2
JF
2106 [NSThread
2107 detachNewThreadSelector:@selector(_readStatus:)
2108 toTarget:self
2109 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2110 ];
ec97ef06 2111
8b29f8e6
JF
2112 _assert(pipe(fds) != -1);
2113 _assert(dup2(fds[0], 0) != -1);
2114 _assert(close(fds[0]) != -1);
2115
2116 input_ = fdopen(fds[1], "a");
2117
36bb2ca2
JF
2118 _assert(pipe(fds) != -1);
2119 _assert(dup2(fds[1], 1) != -1);
2120 _assert(close(fds[1]) != -1);
2121
2122 [NSThread
2123 detachNewThreadSelector:@selector(_readOutput:)
2124 toTarget:self
2125 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2126 ];
ec97ef06
JF
2127 } return self;
2128}
2129
36bb2ca2
JF
2130- (pkgCacheFile &) cache {
2131 return cache_;
ec97ef06
JF
2132}
2133
5a09ae08
JF
2134- (pkgDepCache::Policy *) policy {
2135 return policy_;
2136}
2137
36bb2ca2
JF
2138- (pkgRecords *) records {
2139 return records_;
ec97ef06
JF
2140}
2141
36bb2ca2
JF
2142- (pkgProblemResolver *) resolver {
2143 return resolver_;
ec97ef06
JF
2144}
2145
36bb2ca2
JF
2146- (pkgAcquire &) fetcher {
2147 return *fetcher_;
ec97ef06
JF
2148}
2149
36bb2ca2
JF
2150- (NSArray *) packages {
2151 return packages_;
ec97ef06
JF
2152}
2153
7b0ce2da
JF
2154- (NSArray *) sources {
2155 return [sources_ allValues];
2156}
2157
affeffc7
JF
2158- (NSArray *) issues {
2159 if (cache_->BrokenCount() == 0)
2160 return nil;
2161
2162 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2163
2164 for (Package *package in packages_) {
2165 if (![package broken])
2166 continue;
2167 pkgCache::PkgIterator pkg([package iterator]);
2168
2169 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2170 [entry addObject:[package name]];
2171 [issues addObject:entry];
2172
2173 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2174 if (ver.end())
2175 continue;
2176
2177 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2178 pkgCache::DepIterator start;
2179 pkgCache::DepIterator end;
2180 dep.GlobOr(start, end); // ++dep
2181
2182 if (!cache_->IsImportantDep(end))
2183 continue;
2184 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2185 continue;
2186
2187 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2188 [entry addObject:failure];
2189 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2190
2191 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2192 [failure addObject:[package name]];
2193
2194 pkgCache::PkgIterator target(start.TargetPkg());
2195 if (target->ProvidesList != 0)
2196 [failure addObject:@"?"];
2197 else {
2198 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2199 if (!ver.end())
2200 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2201 else if (!cache_[target].CandidateVerIter(cache_).end())
2202 [failure addObject:@"-"];
2203 else if (target->ProvidesList == 0)
2204 [failure addObject:@"!"];
2205 else
2206 [failure addObject:@"%"];
2207 }
2208
2209 _forever {
2210 if (start.TargetVer() != 0)
2211 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2212 if (start == end)
2213 break;
2214 ++start;
2215 }
2216 }
2217 }
2218
2219 return issues;
2220}
2221
36bb2ca2
JF
2222- (void) reloadData {
2223 _error->Discard();
5a09ae08 2224
36bb2ca2 2225 delete list_;
5a09ae08 2226 list_ = NULL;
36bb2ca2
JF
2227 manager_ = NULL;
2228 delete lock_;
5a09ae08 2229 lock_ = NULL;
36bb2ca2 2230 delete fetcher_;
5a09ae08 2231 fetcher_ = NULL;
36bb2ca2 2232 delete resolver_;
5a09ae08 2233 resolver_ = NULL;
36bb2ca2 2234 delete records_;
5a09ae08
JF
2235 records_ = NULL;
2236 delete policy_;
2237 policy_ = NULL;
36bb2ca2 2238
5a09ae08 2239 cache_.Close();
8b29f8e6 2240
5a09ae08 2241 if (!cache_.Open(progress_, true)) {
8b29f8e6
JF
2242 std::string error;
2243 if (!_error->PopMessage(error))
2244 _assert(false);
5a09ae08 2245 _error->Discard();
c390d3ab 2246 lprintf("cache_.Open():[%s]\n", error.c_str());
5a09ae08
JF
2247
2248 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2249 [delegate_ repairWithSelector:@selector(configure)];
2250 else if (error == "The package lists or status file could not be parsed or opened.")
2251 [delegate_ repairWithSelector:@selector(update)];
9ea8d159
JF
2252 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2253 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2254 // else if (error == "The list of sources could not be read.")
5a09ae08
JF
2255 else _assert(false);
2256
2257 return;
36bb2ca2
JF
2258 }
2259
2260 now_ = [[NSDate date] retain];
2261
5a09ae08 2262 policy_ = new pkgDepCache::Policy();
36bb2ca2
JF
2263 records_ = new pkgRecords(cache_);
2264 resolver_ = new pkgProblemResolver(cache_);
2265 fetcher_ = new pkgAcquire(&status_);
2266 lock_ = NULL;
2267
2268 list_ = new pkgSourceList();
2269 _assert(list_->ReadMainList());
2270
9bedffaa
JF
2271 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2272 _assert(pkgApplyStatus(cache_));
2273
2274 if (cache_->BrokenCount() != 0) {
2275 _assert(pkgFixBroken(cache_));
2276 _assert(cache_->BrokenCount() == 0);
2277 _assert(pkgMinimizeUpgrade(cache_));
2278 }
2279
36bb2ca2
JF
2280 [sources_ removeAllObjects];
2281 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2282 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2283 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2284 [sources_
2285 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2286 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2287 ];
2288 }
2289
2290 [packages_ removeAllObjects];
2291 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
5a09ae08
JF
2292 if (Package *package = [Package packageWithIterator:iterator database:self])
2293 [packages_ addObject:package];
ab398adf
JF
2294
2295 [packages_ sortUsingSelector:@selector(compareByName:)];
ec97ef06
JF
2296}
2297
5a09ae08
JF
2298- (void) configure {
2299 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2300 system([dpkg UTF8String]);
2301}
2302
77fcccaf
JF
2303- (void) clean {
2304 if (lock_ != NULL)
2305 return;
2306
2307 FileFd Lock;
2308 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2309 _assert(!_error->PendingError());
2310
2311 pkgAcquire fetcher;
2312 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2313
9bedffaa
JF
2314 class LogCleaner :
2315 public pkgArchiveCleaner
2316 {
77fcccaf
JF
2317 protected:
2318 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
9bedffaa 2319 unlink(File);
77fcccaf
JF
2320 }
2321 } cleaner;
2322
2323 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2324 std::string error;
2325 while (_error->PopMessage(error))
c390d3ab 2326 lprintf("ArchiveCleaner: %s\n", error.c_str());
77fcccaf
JF
2327 }
2328}
2329
36bb2ca2
JF
2330- (void) prepare {
2331 pkgRecords records(cache_);
2332
2333 lock_ = new FileFd();
2334 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2335 _assert(!_error->PendingError());
2336
2337 pkgSourceList list;
2338 // XXX: explain this with an error message
2339 _assert(list.ReadMainList());
2340
2341 manager_ = (_system->CreatePM(cache_));
2342 _assert(manager_->GetArchives(fetcher_, &list, &records));
2343 _assert(!_error->PendingError());
ec97ef06
JF
2344}
2345
36bb2ca2 2346- (void) perform {
a72074b2
JF
2347 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2348 pkgSourceList list;
2349 _assert(list.ReadMainList());
2350 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2351 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2352 }
a0be02eb 2353
eeb9b112
JF
2354 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2355 _trace();
36bb2ca2 2356 return;
eeb9b112
JF
2357 }
2358
2359 bool failed = false;
2360 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2361 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2362 continue;
2363
2364 std::string uri = (*item)->DescURI();
2365 std::string error = (*item)->ErrorText;
2366
c390d3ab 2367 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
eeb9b112
JF
2368 failed = true;
2369
2370 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2371 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2372 waitUntilDone:YES
2373 ];
2374 }
2375
2376 if (failed) {
2377 _trace();
2378 return;
2379 }
36bb2ca2
JF
2380
2381 _system->UnLock();
2382 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2383
eeb9b112
JF
2384 if (_error->PendingError()) {
2385 _trace();
36bb2ca2 2386 return;
eeb9b112
JF
2387 }
2388
2389 if (result == pkgPackageManager::Failed) {
2390 _trace();
36bb2ca2 2391 return;
eeb9b112
JF
2392 }
2393
2394 if (result != pkgPackageManager::Completed) {
2395 _trace();
36bb2ca2 2396 return;
eeb9b112 2397 }
a0be02eb 2398
a72074b2
JF
2399 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2400 pkgSourceList list;
2401 _assert(list.ReadMainList());
2402 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2403 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2404 }
2405
2406 if (![before isEqualToArray:after])
2407 [self update];
ec97ef06
JF
2408}
2409
36bb2ca2 2410- (void) upgrade {
36bb2ca2 2411 _assert(pkgDistUpgrade(cache_));
c7c6384e
JF
2412}
2413
36bb2ca2
JF
2414- (void) update {
2415 [self updateWithStatus:status_];
2416}
b4d89997 2417
36bb2ca2
JF
2418- (void) updateWithStatus:(Status &)status {
2419 pkgSourceList list;
2420 _assert(list.ReadMainList());
b4d89997 2421
36bb2ca2
JF
2422 FileFd lock;
2423 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2424 _assert(!_error->PendingError());
2425
2426 pkgAcquire fetcher(&status);
2427 _assert(list.GetIndexes(&fetcher));
2428
2429 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2430 bool failed = false;
2431 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2432 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2433 (*item)->Finished();
2434 failed = true;
2435 }
2436
2437 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2438 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2439 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
b4d89997 2440 }
36bb2ca2
JF
2441
2442 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
bbb879fb 2443 Changed_ = true;
b4d89997
JF
2444 }
2445}
2446
36bb2ca2
JF
2447- (void) setDelegate:(id)delegate {
2448 delegate_ = delegate;
2449 status_.setDelegate(delegate);
2450 progress_.setDelegate(delegate);
2451}
b4d89997 2452
36bb2ca2
JF
2453- (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2454 pkgIndexFile *index(NULL);
2455 list_->FindIndex(file, index);
2456 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
b19871dd
JF
2457}
2458
36bb2ca2
JF
2459@end
2460/* }}} */
b4d89997 2461
affeffc7 2462/* PopUp Windows {{{ */
c390d3ab
JF
2463@interface PopUpView : UIView {
2464 _transient id delegate_;
2465 UITransitionView *transition_;
2466 UIView *overlay_;
2467}
2468
2469- (void) cancel;
2470- (id) initWithView:(UIView *)view delegate:(id)delegate;
2471
2472@end
2473
2474@implementation PopUpView
2475
2476- (void) dealloc {
2477 [transition_ setDelegate:nil];
2478 [transition_ release];
2479 [overlay_ release];
2480 [super dealloc];
2481}
2482
2483- (void) cancel {
2484 [transition_ transition:UITransitionPushFromTop toView:nil];
2485}
2486
2487- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2488 if (from != nil && to == nil)
2489 [self removeFromSuperview];
2490}
2491
2492- (id) initWithView:(UIView *)view delegate:(id)delegate {
2493 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2494 delegate_ = delegate;
2495
2496 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2497 [self addSubview:transition_];
2498
2499 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2500
2501 [view addSubview:self];
2502
2503 [transition_ setDelegate:self];
2504
2505 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2506 [transition_ transition:UITransitionNone toView:blank];
2507 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2508 } return self;
2509}
2510
2511@end
2512/* }}} */
2513
2514/* Mail Composition {{{ */
2515@interface MailToView : PopUpView {
2516 MailComposeController *controller_;
2517}
2518
2519- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2520
2521@end
2522
2523@implementation MailToView
2524
2525- (void) dealloc {
2526 [controller_ release];
2527 [super dealloc];
2528}
2529
c390d3ab
JF
2530- (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2531 NSLog(@"will");
2532}
2533
2534- (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2535 NSLog(@"did:%@", delivery);
2536// [UIApp setStatusBarShowsProgress:NO];
2537if ([controller error]){
2538NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2539UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2540[mailAlertSheet setBodyText:[controller error]];
2541[mailAlertSheet popupAlertAnimated:YES];
2542}
2543}
2544
affeffc7
JF
2545- (void) showError {
2546 NSLog(@"%@", [controller_ error]);
2547 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2548 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2549 [mailAlertSheet setBodyText:[controller_ error]];
2550 [mailAlertSheet popupAlertAnimated:YES];
2551}
2552
2553- (void) deliverMessage { _pooled
86316a91
JF
2554 setuid(501);
2555 setgid(501);
2556
affeffc7
JF
2557 if (![controller_ deliverMessage])
2558 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2559}
2560
c390d3ab 2561- (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
affeffc7
JF
2562 if ([controller_ needsDelivery])
2563 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
c390d3ab
JF
2564 else
2565 [self cancel];
2566}
2567
2568- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2569 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2570 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2571 [controller_ setDelegate:self];
2572 [controller_ initializeUI];
2573 [controller_ setupForURL:url];
2574
2575 UIView *view([controller_ view]);
2576 [overlay_ addSubview:view];
2577 } return self;
2578}
2579
2580@end
2581/* }}} */
36bb2ca2 2582/* Confirmation View {{{ */
a9a0661e
JF
2583bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2584 if (!iterator.end())
2585 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2586 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2587 continue;
2588 pkgCache::PkgIterator package(dep.TargetPkg());
2589 if (package.end())
2590 continue;
2591 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2592 return true;
2593 }
2594
2595 return false;
2596}
2597
36bb2ca2
JF
2598@protocol ConfirmationViewDelegate
2599- (void) cancel;
2600- (void) confirm;
2601@end
2367a917 2602
affeffc7 2603@interface ConfirmationView : BrowserView {
770f2a8e 2604 _transient Database *database_;
a9543575 2605 UIActionSheet *essential_;
affeffc7
JF
2606 NSArray *changes_;
2607 NSArray *issues_;
2608 NSArray *sizes_;
a9a0661e 2609 BOOL substrate_;
36bb2ca2 2610}
dc5812ec 2611
affeffc7 2612- (id) initWithBook:(RVBook *)book database:(Database *)database;
b4d89997 2613
dc5812ec
JF
2614@end
2615
36bb2ca2 2616@implementation ConfirmationView
dc5812ec 2617
2367a917 2618- (void) dealloc {
affeffc7
JF
2619 [changes_ release];
2620 if (issues_ != nil)
2621 [issues_ release];
2622 [sizes_ release];
36bb2ca2
JF
2623 if (essential_ != nil)
2624 [essential_ release];
2367a917
JF
2625 [super dealloc];
2626}
2627
36bb2ca2 2628- (void) cancel {
36bb2ca2 2629 [delegate_ cancel];
affeffc7 2630 [book_ popFromSuperviewAnimated:YES];
36bb2ca2
JF
2631}
2632
a9543575 2633- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
9bedffaa
JF
2634 NSString *context = [sheet context];
2635
2636 if ([context isEqualToString:@"remove"])
2637 switch (button) {
2638 case 1:
31f3cfff 2639 [self cancel];
9bedffaa
JF
2640 break;
2641 case 2:
a9a0661e
JF
2642 if (substrate_)
2643 Finish_ = 2;
31f3cfff 2644 [delegate_ confirm];
9bedffaa
JF
2645 break;
2646 default:
2647 _assert(false);
2648 }
2649 else if ([context isEqualToString:@"unable"])
2650 [self cancel];
2651
2652 [sheet dismiss];
36bb2ca2
JF
2653}
2654
affeffc7
JF
2655- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2656 [window setValue:changes_ forKey:@"changes"];
2657 [window setValue:issues_ forKey:@"issues"];
2658 [window setValue:sizes_ forKey:@"sizes"];
2659 [super webView:sender didClearWindowObject:window forFrame:frame];
36bb2ca2
JF
2660}
2661
affeffc7 2662- (id) initWithBook:(RVBook *)book database:(Database *)database {
770f2a8e
JF
2663 if ((self = [super initWithBook:book]) != nil) {
2664 database_ = database;
2665
36bb2ca2
JF
2666 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2667 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2668 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2669 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2670 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
dc5812ec 2671
36bb2ca2 2672 bool remove(false);
dc5812ec 2673
a9a0661e
JF
2674 pkgDepCache::Policy *policy([database_ policy]);
2675
36bb2ca2 2676 pkgCacheFile &cache([database_ cache]);
2388b078
JF
2677 NSArray *packages = [database_ packages];
2678 for (size_t i(0), e = [packages count]; i != e; ++i) {
2679 Package *package = [packages objectAtIndex:i];
2680 pkgCache::PkgIterator iterator = [package iterator];
36bb2ca2 2681 pkgDepCache::StateCache &state(cache[iterator]);
dc5812ec 2682
2388b078
JF
2683 NSString *name([package name]);
2684
36bb2ca2
JF
2685 if (state.NewInstall())
2686 [installing addObject:name];
2687 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2688 [reinstalling addObject:name];
2689 else if (state.Upgrade())
2690 [upgrading addObject:name];
2691 else if (state.Downgrade())
2692 [downgrading addObject:name];
2693 else if (state.Delete()) {
2388b078 2694 if ([package essential])
36bb2ca2
JF
2695 remove = true;
2696 [removing addObject:name];
a9a0661e
JF
2697 } else continue;
2698
2699 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2700 substrate_ |= DepSubstrate(iterator.CurrentVer());
36bb2ca2 2701 }
ec97ef06 2702
36bb2ca2
JF
2703 if (!remove)
2704 essential_ = nil;
9bedffaa 2705 else if (Advanced_ || true) {
a9543575 2706 essential_ = [[UIActionSheet alloc]
31f3cfff 2707 initWithTitle:@"Removing Essentials"
9bedffaa 2708 buttons:[NSArray arrayWithObjects:
31f3cfff
JF
2709 @"Cancel Operation (Safe)",
2710 @"Force Removal (Unsafe)",
9bedffaa 2711 nil]
31f3cfff 2712 defaultButtonIndex:0
9bedffaa
JF
2713 delegate:self
2714 context:@"remove"
2715 ];
2716
199d0ba5 2717#ifndef __OBJC2__
9bedffaa 2718 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
199d0ba5 2719#endif
31f3cfff 2720 [essential_ setBodyText:@"This operation involves the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. If you continue, you may not be able to use Cydia to repair any damage."];
9bedffaa 2721 } else {
a9543575 2722 essential_ = [[UIActionSheet alloc]
36bb2ca2
JF
2723 initWithTitle:@"Unable to Comply"
2724 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2725 defaultButtonIndex:0
2726 delegate:self
9bedffaa 2727 context:@"unable"
36bb2ca2 2728 ];
ec97ef06 2729
31f3cfff 2730 [essential_ setBodyText:@"This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. In order to continue and force this operation you will need to be activate the Advanced mode under to continue and force this operation you will need to be activate the Advanced mode under Settings."];
36bb2ca2 2731 }
ec97ef06 2732
affeffc7
JF
2733 changes_ = [[NSArray alloc] initWithObjects:
2734 installing,
2735 reinstalling,
2736 upgrading,
2737 downgrading,
2738 removing,
2739 nil];
dc5812ec 2740
affeffc7
JF
2741 issues_ = [database_ issues];
2742 if (issues_ != nil)
2743 issues_ = [issues_ retain];
dc5812ec 2744
affeffc7
JF
2745 sizes_ = [[NSArray alloc] initWithObjects:
2746 SizeString([database_ fetcher].FetchNeeded()),
2747 SizeString([database_ fetcher].PartialPresent()),
2748 SizeString([database_ cache]->UsrSize()),
2749 nil];
dc5812ec 2750
affeffc7 2751 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
36bb2ca2 2752 } return self;
b4d89997 2753}
8da60fb7 2754
affeffc7
JF
2755- (NSString *) backButtonTitle {
2756 return @"Confirm";
2757}
2758
2759- (NSString *) leftButtonTitle {
2760 return @"Cancel";
2761}
2762
2763- (NSString *) _rightButtonTitle {
2764 return issues_ == nil ? @"Confirm" : nil;
2765}
2766
2767- (void) _leftButtonClicked {
2768 [self cancel];
2769}
2770
2771- (void) _rightButtonClicked {
2772 if (essential_ != nil)
2773 [essential_ popupAlertAnimated:YES];
2774 else {
2775 if (substrate_)
2776 Finish_ = 2;
2777 [delegate_ confirm];
2778 }
2779}
2780
36bb2ca2
JF
2781@end
2782/* }}} */
8da60fb7 2783
36bb2ca2
JF
2784/* Progress Data {{{ */
2785@interface ProgressData : NSObject {
2786 SEL selector_;
2787 id target_;
2788 id object_;
dc5812ec
JF
2789}
2790
36bb2ca2 2791- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
b4d89997 2792
36bb2ca2
JF
2793- (SEL) selector;
2794- (id) target;
2795- (id) object;
2796@end
b4d89997 2797
36bb2ca2 2798@implementation ProgressData
b4d89997 2799
36bb2ca2
JF
2800- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2801 if ((self = [super init]) != nil) {
2802 selector_ = selector;
2803 target_ = target;
2804 object_ = object;
2805 } return self;
2806}
b4d89997 2807
36bb2ca2
JF
2808- (SEL) selector {
2809 return selector_;
2810}
b4d89997 2811
36bb2ca2
JF
2812- (id) target {
2813 return target_;
8da60fb7
JF
2814}
2815
36bb2ca2
JF
2816- (id) object {
2817 return object_;
dc5812ec
JF
2818}
2819
2367a917
JF
2820@end
2821/* }}} */
36bb2ca2
JF
2822/* Progress View {{{ */
2823@interface ProgressView : UIView <
8b29f8e6 2824 ConfigurationDelegate,
36bb2ca2
JF
2825 ProgressDelegate
2826> {
8b29f8e6 2827 _transient Database *database_;
36bb2ca2
JF
2828 UIView *view_;
2829 UIView *background_;
2830 UITransitionView *transition_;
2831 UIView *overlay_;
2832 UINavigationBar *navbar_;
2833 UIProgressBar *progress_;
2834 UITextView *output_;
2835 UITextLabel *status_;
31f3cfff 2836 UIPushButton *close_;
36bb2ca2 2837 id delegate_;
bd150f54 2838 BOOL running_;
dddbc481 2839 SHA1SumValue springlist_;
affeffc7 2840 SHA1SumValue sandplate_;
baf80942
JF
2841 size_t received_;
2842 NSTimeInterval last_;
2367a917
JF
2843}
2844
36bb2ca2 2845- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2367a917 2846
8b29f8e6 2847- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
36bb2ca2
JF
2848- (void) setContentView:(UIView *)view;
2849- (void) resetView;
2367a917 2850
36bb2ca2
JF
2851- (void) _retachThread;
2852- (void) _detachNewThreadData:(ProgressData *)data;
2853- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2367a917 2854
bd150f54
JF
2855- (BOOL) isRunning;
2856
2367a917
JF
2857@end
2858
36bb2ca2
JF
2859@protocol ProgressViewDelegate
2860- (void) progressViewIsComplete:(ProgressView *)sender;
2861@end
2862
2863@implementation ProgressView
2367a917
JF
2864
2865- (void) dealloc {
36bb2ca2
JF
2866 [transition_ setDelegate:nil];
2867 [navbar_ setDelegate:nil];
2868
2869 [view_ release];
2870 if (background_ != nil)
2871 [background_ release];
2872 [transition_ release];
2873 [overlay_ release];
2874 [navbar_ release];
2875 [progress_ release];
2876 [output_ release];
2877 [status_ release];
31f3cfff 2878 [close_ release];
2367a917
JF
2879 [super dealloc];
2880}
2881
36bb2ca2
JF
2882- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2883 if (bootstrap_ && from == overlay_ && to == view_)
2884 exit(0);
2885}
2367a917 2886
8b29f8e6 2887- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
36bb2ca2 2888 if ((self = [super initWithFrame:frame]) != nil) {
8b29f8e6 2889 database_ = database;
36bb2ca2 2890 delegate_ = delegate;
ec97ef06 2891
36bb2ca2
JF
2892 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2893 [transition_ setDelegate:self];
2367a917 2894
36bb2ca2 2895 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
ec97ef06 2896
36bb2ca2 2897 if (bootstrap_)
baf80942 2898 [overlay_ setBackgroundColor:[UIColor blackColor]];
36bb2ca2
JF
2899 else {
2900 background_ = [[UIView alloc] initWithFrame:[self bounds]];
baf80942 2901 [background_ setBackgroundColor:[UIColor blackColor]];
36bb2ca2
JF
2902 [self addSubview:background_];
2903 }
ec97ef06 2904
36bb2ca2 2905 [self addSubview:transition_];
2367a917 2906
36bb2ca2
JF
2907 CGSize navsize = [UINavigationBar defaultSize];
2908 CGRect navrect = {{0, 0}, navsize};
2367a917 2909
36bb2ca2
JF
2910 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2911 [overlay_ addSubview:navbar_];
b4d89997 2912
36bb2ca2
JF
2913 [navbar_ setBarStyle:1];
2914 [navbar_ setDelegate:self];
2367a917 2915
36bb2ca2
JF
2916 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2917 [navbar_ pushNavigationItem:navitem];
c7c6384e 2918
36bb2ca2
JF
2919 CGRect bounds = [overlay_ bounds];
2920 CGSize prgsize = [UIProgressBar defaultSize];
ec97ef06 2921
36bb2ca2
JF
2922 CGRect prgrect = {{
2923 (bounds.size.width - prgsize.width) / 2,
2924 bounds.size.height - prgsize.height - 20
2925 }, prgsize};
ec97ef06 2926
36bb2ca2 2927 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
31f3cfff 2928 [progress_ setStyle:0];
ec97ef06 2929
36bb2ca2
JF
2930 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2931 10,
2932 bounds.size.height - prgsize.height - 50,
2933 bounds.size.width - 20,
2934 24
2935 )];
ec97ef06 2936
baf80942
JF
2937 [status_ setColor:[UIColor whiteColor]];
2938 [status_ setBackgroundColor:[UIColor clearColor]];
ec97ef06 2939
36bb2ca2
JF
2940 [status_ setCentersHorizontally:YES];
2941 //[status_ setFont:font];
ec97ef06 2942
36bb2ca2
JF
2943 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2944 10,
2945 navrect.size.height + 20,
2946 bounds.size.width - 20,
2947 bounds.size.height - navsize.height - 62 - navrect.size.height
2948 )];
1d80f6b9 2949
36bb2ca2
JF
2950 //[output_ setTextFont:@"Courier New"];
2951 [output_ setTextSize:12];
2367a917 2952
baf80942
JF
2953 [output_ setTextColor:[UIColor whiteColor]];
2954 [output_ setBackgroundColor:[UIColor clearColor]];
2367a917 2955
36bb2ca2
JF
2956 [output_ setMarginTop:0];
2957 [output_ setAllowsRubberBanding:YES];
795d26fc 2958 [output_ setEditable:NO];
b4d89997 2959
36bb2ca2 2960 [overlay_ addSubview:output_];
2367a917 2961
31f3cfff
JF
2962 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2963 10,
2964 bounds.size.height - prgsize.height - 50,
2965 bounds.size.width - 20,
795d26fc 2966 32 + prgsize.height
31f3cfff
JF
2967 )];
2968
2969 [close_ setAutosizesToFit:NO];
2970 [close_ setDrawsShadow:YES];
2971 [close_ setStretchBackground:YES];
31f3cfff
JF
2972 [close_ setEnabled:YES];
2973
a9543575 2974 UIFont *bold = [UIFont boldSystemFontOfSize:22];
31f3cfff 2975 [close_ setTitleFont:bold];
31f3cfff
JF
2976
2977 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2978 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2979 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
36bb2ca2 2980 } return self;
2367a917
JF
2981}
2982
36bb2ca2
JF
2983- (void) setContentView:(UIView *)view {
2984 view_ = [view retain];
2367a917
JF
2985}
2986
36bb2ca2
JF
2987- (void) resetView {
2988 [transition_ transition:6 toView:view_];
2367a917
JF
2989}
2990
a9543575 2991- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
8b29f8e6
JF
2992 NSString *context = [sheet context];
2993 if ([context isEqualToString:@"conffile"]) {
2994 FILE *input = [database_ input];
2995
2996 switch (button) {
2997 case 1:
2998 fprintf(input, "N\n");
2999 fflush(input);
9bedffaa 3000 break;
8b29f8e6
JF
3001 case 2:
3002 fprintf(input, "Y\n");
3003 fflush(input);
9bedffaa
JF
3004 break;
3005 default:
3006 _assert(false);
8b29f8e6
JF
3007 }
3008 }
3009
36bb2ca2 3010 [sheet dismiss];
2367a917
JF
3011}
3012
31f3cfff 3013- (void) closeButtonPushed {
affeffc7
JF
3014 running_ = NO;
3015
bc8cd583
JF
3016 switch (Finish_) {
3017 case 0:
a9a0661e 3018 [self resetView];
bc8cd583
JF
3019 break;
3020
3021 case 1:
3022 [delegate_ suspendWithAnimation:YES];
3023 break;
3024
3025 case 2:
a9a0661e 3026 system("launchctl stop com.apple.SpringBoard");
bc8cd583
JF
3027 break;
3028
3029 case 3:
affeffc7 3030 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
bc8cd583
JF
3031 break;
3032
3033 case 4:
3034 system("reboot");
3035 break;
3036 }
b19871dd
JF
3037}
3038
31f3cfff 3039- (void) _retachThread {
795d26fc
JF
3040 UINavigationItem *item = [navbar_ topItem];
3041 [item setTitle:@"Complete"];
3042
31f3cfff
JF
3043 [overlay_ addSubview:close_];
3044 [progress_ removeFromSuperview];
3045 [status_ removeFromSuperview];
bd150f54 3046
c390d3ab
JF
3047 [delegate_ progressViewIsComplete:self];
3048
affeffc7
JF
3049 if (Finish_ < 4) {
3050 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3051 MMap mmap(file, MMap::ReadOnly);
3052 SHA1Summation sha1;
3053 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3054 if (!(sandplate_ == sha1.Result()))
3055 Finish_ = 4;
3056 }
3057
3058 if (Finish_ < 3) {
3059 FileFd file(SpringBoard_, FileFd::ReadOnly);
dddbc481
JF
3060 MMap mmap(file, MMap::ReadOnly);
3061 SHA1Summation sha1;
3062 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3063 if (!(springlist_ == sha1.Result()))
3064 Finish_ = 3;
3065 }
3066
bc8cd583
JF
3067 switch (Finish_) {
3068 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3069 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3070 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3071 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3072 case 4: [close_ setTitle:@"Reboot Device"]; break;
3073 }
3074
87f46a96
JF
3075#define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3076
95cafe4f 3077 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
87f46a96
JF
3078 [cache autorelease];
3079
3080 NSFileManager *manager = [NSFileManager defaultManager];
6e201c55 3081 NSError *error = nil;
87f46a96 3082
6e201c55 3083 id system = [cache objectForKey:@"System"];
87f46a96
JF
3084 if (system == nil)
3085 goto error;
3086
3087 struct stat info;
3088 if (stat(Cache_, &info) == -1)
3089 goto error;
3090
3091 [system removeAllObjects];
3092
6e201c55 3093 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
87f46a96
JF
3094 for (NSString *app in apps)
3095 if ([app hasSuffix:@".app"]) {
3096 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3097 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3098 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3099 [info autorelease];
3100 [info setObject:path forKey:@"Path"];
3101 [info setObject:@"System" forKey:@"ApplicationType"];
6e201c55 3102 [system addInfoDictionary:info];
87f46a96
JF
3103 }
3104 }
6e201c55 3105 } else goto error;
87f46a96
JF
3106
3107 [cache writeToFile:@Cache_ atomically:YES];
3108
3109 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3110 goto error;
3111 if (chmod(Cache_, info.st_mode) == -1)
3112 goto error;
3113
3114 if (false) error:
c390d3ab 3115 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
87f46a96
JF
3116 }
3117
c4ce98df 3118 notify_post("com.apple.mobile.application_installed");
c4ce98df 3119
bd150f54 3120 [delegate_ setStatusBarShowsProgress:NO];
31f3cfff
JF
3121}
3122
affeffc7 3123- (void) _detachNewThreadData:(ProgressData *)data { _pooled
36bb2ca2
JF
3124 [[data target] performSelector:[data selector] withObject:[data object]];
3125 [data release];
b19871dd 3126
36bb2ca2 3127 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
b19871dd
JF
3128}
3129
36bb2ca2 3130- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
5a09ae08
JF
3131 UINavigationItem *item = [navbar_ topItem];
3132 [item setTitle:title];
b4d89997 3133
36bb2ca2
JF
3134 [status_ setText:nil];
3135 [output_ setText:@""];
3136 [progress_ setProgress:0];
b19871dd 3137
baf80942
JF
3138 received_ = 0;
3139 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3140
31f3cfff
JF
3141 [close_ removeFromSuperview];
3142 [overlay_ addSubview:progress_];
3143 [overlay_ addSubview:status_];
3144
bd150f54
JF
3145 [delegate_ setStatusBarShowsProgress:YES];
3146 running_ = YES;
3147
dddbc481 3148 {
affeffc7
JF
3149 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3150 MMap mmap(file, MMap::ReadOnly);
3151 SHA1Summation sha1;
3152 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3153 sandplate_ = sha1.Result();
3154 }
3155
3156 {
3157 FileFd file(SpringBoard_, FileFd::ReadOnly);
dddbc481
JF
3158 MMap mmap(file, MMap::ReadOnly);
3159 SHA1Summation sha1;
3160 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3161 springlist_ = sha1.Result();
3162 }
3163
36bb2ca2 3164 [transition_ transition:6 toView:overlay_];
b19871dd 3165
36bb2ca2
JF
3166 [NSThread
3167 detachNewThreadSelector:@selector(_detachNewThreadData:)
3168 toTarget:self
3169 withObject:[[ProgressData alloc]
3170 initWithSelector:selector
3171 target:target
3172 object:object
3173 ]
3174 ];
b19871dd
JF
3175}
3176
5a09ae08
JF
3177- (void) repairWithSelector:(SEL)selector {
3178 [self
3179 detachNewThreadSelector:selector
3180 toTarget:database_
3181 withObject:nil
baf80942 3182 title:@"Repairing"
5a09ae08
JF
3183 ];
3184}
3185
8b29f8e6
JF
3186- (void) setConfigurationData:(NSString *)data {
3187 [self
3188 performSelectorOnMainThread:@selector(_setConfigurationData:)
3189 withObject:data
3190 waitUntilDone:YES
3191 ];
3192}
3193
31f3cfff
JF
3194- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3195 Package *package = id == nil ? nil : [database_ packageWithName:id];
3196
a9543575 3197 UIActionSheet *sheet = [[[UIActionSheet alloc]
affeffc7 3198 initWithTitle:(package == nil ? id : [package name])
31f3cfff
JF
3199 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3200 defaultButtonIndex:0
3201 delegate:self
3202 context:@"error"
3203 ] autorelease];
3204
3205 [sheet setBodyText:error];
3206 [sheet popupAlertAnimated:YES];
b19871dd
JF
3207}
3208
36bb2ca2
JF
3209- (void) setProgressTitle:(NSString *)title {
3210 [self
3211 performSelectorOnMainThread:@selector(_setProgressTitle:)
3212 withObject:title
3213 waitUntilDone:YES
3214 ];
b19871dd
JF
3215}
3216
36bb2ca2
JF
3217- (void) setProgressPercent:(float)percent {
3218 [self
3219 performSelectorOnMainThread:@selector(_setProgressPercent:)
3220 withObject:[NSNumber numberWithFloat:percent]
3221 waitUntilDone:YES
3222 ];
b19871dd
JF
3223}
3224
baf80942
JF
3225- (void) startProgress {
3226 last_ = [NSDate timeIntervalSinceReferenceDate];
3227}
3228
36bb2ca2
JF
3229- (void) addProgressOutput:(NSString *)output {
3230 [self
3231 performSelectorOnMainThread:@selector(_addProgressOutput:)
3232 withObject:output
3233 waitUntilDone:YES
3234 ];
b19871dd
JF
3235}
3236
baf80942
JF
3237- (bool) isCancelling:(size_t)received {
3238 if (last_ != 0) {
3239 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3240 if (received_ != received) {
3241 received_ = received;
3242 last_ = now;
3243 } else if (now - last_ > 30)
3244 return true;
3245 }
3246
3247 return false;
3248}
3249
8b29f8e6 3250- (void) _setConfigurationData:(NSString *)data {
7b0ce2da
JF
3251 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3252
8b29f8e6
JF
3253 _assert(conffile_r(data));
3254
3255 NSString *ofile = conffile_r[1];
3256 //NSString *nfile = conffile_r[2];
3257
a9543575 3258 UIActionSheet *sheet = [[[UIActionSheet alloc]
8b29f8e6
JF
3259 initWithTitle:@"Configuration Upgrade"
3260 buttons:[NSArray arrayWithObjects:
3261 @"Keep My Old Copy",
3262 @"Accept The New Copy",
3263 // XXX: @"See What Changed",
3264 nil]
3265 defaultButtonIndex:0
3266 delegate:self
3267 context:@"conffile"
3268 ] autorelease];
3269
3270 [sheet setBodyText:[NSString stringWithFormat:
3271 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3272 , ofile]];
3273
3274 [sheet popupAlertAnimated:YES];
3275}
3276
36bb2ca2 3277- (void) _setProgressTitle:(NSString *)title {
baf80942 3278 [status_ setText:title];
b4d89997 3279}
b19871dd 3280
36bb2ca2
JF
3281- (void) _setProgressPercent:(NSNumber *)percent {
3282 [progress_ setProgress:[percent floatValue]];
49048579
JF
3283}
3284
36bb2ca2
JF
3285- (void) _addProgressOutput:(NSString *)output {
3286 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3287 CGSize size = [output_ contentSize];
3288 CGRect rect = {{0, size.height}, {size.width, 0}};
3289 [output_ scrollRectToVisible:rect animated:YES];
3290}
dc088e63 3291
bd150f54 3292- (BOOL) isRunning {
c4ce98df 3293 return running_;
bd150f54
JF
3294}
3295
36bb2ca2
JF
3296@end
3297/* }}} */
dc088e63 3298
36bb2ca2 3299/* Package Cell {{{ */
f641a0e5
JF
3300@interface PackageCell : UISimpleTableCell {
3301 UIImage *icon_;
3302 NSString *name_;
3303 NSString *description_;
3304 NSString *source_;
c390d3ab 3305 UIImage *badge_;
31f3cfff 3306#ifdef USE_BADGES
9bedffaa 3307 UITextLabel *status_;
31f3cfff 3308#endif
36bb2ca2 3309}
723a0072 3310
36bb2ca2
JF
3311- (PackageCell *) init;
3312- (void) setPackage:(Package *)package;
ec97ef06 3313
9bedffaa
JF
3314+ (int) heightForPackage:(Package *)package;
3315
36bb2ca2 3316@end
49048579 3317
36bb2ca2
JF
3318@implementation PackageCell
3319
f641a0e5
JF
3320- (void) clearPackage {
3321 if (icon_ != nil) {
3322 [icon_ release];
3323 icon_ = nil;
3324 }
3325
3326 if (name_ != nil) {
3327 [name_ release];
3328 name_ = nil;
3329 }
3330
3331 if (description_ != nil) {
3332 [description_ release];
3333 description_ = nil;
3334 }
3335
3336 if (source_ != nil) {
3337 [source_ release];
3338 source_ = nil;
3339 }
c390d3ab
JF
3340
3341 if (badge_ != nil) {
3342 [badge_ release];
3343 badge_ = nil;
3344 }
f641a0e5
JF
3345}
3346
36bb2ca2 3347- (void) dealloc {
f641a0e5 3348 [self clearPackage];
31f3cfff 3349#ifdef USE_BADGES
9bedffaa 3350 [status_ release];
31f3cfff 3351#endif
36bb2ca2 3352 [super dealloc];
b4d89997 3353}
b19871dd 3354
36bb2ca2
JF
3355- (PackageCell *) init {
3356 if ((self = [super init]) != nil) {
31f3cfff 3357#ifdef USE_BADGES
9bedffaa 3358 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
baf80942 3359 [status_ setBackgroundColor:[UIColor clearColor]];
9bedffaa 3360 [status_ setFont:small];
31f3cfff 3361#endif
36bb2ca2 3362 } return self;
b4d89997 3363}
b19871dd 3364
36bb2ca2 3365- (void) setPackage:(Package *)package {
46dbfd32 3366 [self clearPackage];
31f3cfff 3367
36bb2ca2 3368 Source *source = [package source];
c390d3ab
JF
3369 NSString *section = [package section];
3370 if (section != nil)
3371 section = Simplify(section);
b19871dd 3372
770f2a8e 3373 icon_ = [[package icon] retain];
b19871dd 3374
f641a0e5
JF
3375 name_ = [[package name] retain];
3376 description_ = [[package tagline] retain];
36bb2ca2 3377
a54b1c10
JF
3378 NSString *label = nil;
3379 bool trusted = false;
b19871dd 3380
36bb2ca2
JF
3381 if (source != nil) {
3382 label = [source label];
3383 trusted = [source trusted];
a54b1c10 3384 } else if ([[package id] isEqualToString:@"firmware"])
36bb2ca2 3385 label = @"Apple";
7b0ce2da 3386 else
36bb2ca2 3387 label = @"Unknown/Local";
b19871dd 3388
a54b1c10
JF
3389 NSString *from = [NSString stringWithFormat:@"from %@", label];
3390
a54b1c10
JF
3391 if (section != nil && ![section isEqualToString:label])
3392 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3393
f641a0e5 3394 source_ = [from retain];
36bb2ca2 3395
c390d3ab
JF
3396 if (NSString *purpose = [package primaryPurpose])
3397 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3398 badge_ = [badge_ retain];
3399
31f3cfff 3400#ifdef USE_BADGES
9bedffaa
JF
3401 if (NSString *mode = [package mode]) {
3402 [badge_ setImage:[UIImage applicationImageNamed:
3403 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3404 ]];
3405
3406 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
affeffc7 3407 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
9bedffaa
JF
3408 } else if ([package half]) {
3409 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3410 [status_ setText:@"Package Damaged"];
baf80942 3411 [status_ setColor:[UIColor redColor]];
9bedffaa
JF
3412 } else {
3413 [badge_ setImage:nil];
3414 [status_ setText:nil];
3415 }
31f3cfff 3416#endif
b19871dd
JF
3417}
3418
f641a0e5 3419- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
baf80942
JF
3420 if (icon_ != nil) {
3421 CGRect rect;
3422 rect.size = [icon_ size];
3423
3424 rect.size.width /= 2;
3425 rect.size.height /= 2;
3426
3427 rect.origin.x = 25 - rect.size.width / 2;
3428 rect.origin.y = 25 - rect.size.height / 2;
3429
3430 [icon_ drawInRect:rect];
3431 }
b19871dd 3432
c390d3ab
JF
3433 if (badge_ != nil) {
3434 CGSize size = [badge_ size];
3435
3436 [badge_ drawAtPoint:CGPointMake(
3437 36 - size.width / 2,
3438 36 - size.height / 2
3439 )];
3440 }
3441
f641a0e5
JF
3442 if (selected)
3443 UISetColor(White_);
b19871dd 3444
f641a0e5
JF
3445 if (!selected)
3446 UISetColor(Black_);
3447 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3448 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
b19871dd 3449
f641a0e5
JF
3450 if (!selected)
3451 UISetColor(Gray_);
3452 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
b19871dd 3453
f641a0e5 3454 [super drawContentInRect:rect selected:selected];
ec97ef06
JF
3455}
3456
9bedffaa 3457+ (int) heightForPackage:(Package *)package {
6d9712c4 3458 NSString *tagline([package tagline]);
f641a0e5 3459 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
31f3cfff 3460#ifdef USE_BADGES
9bedffaa 3461 if ([package hasMode] || [package half])
6d9712c4 3462 return height + 96;
9bedffaa 3463 else
31f3cfff 3464#endif
6d9712c4 3465 return height + 73;
9bedffaa
JF
3466}
3467
8da60fb7
JF
3468@end
3469/* }}} */
36bb2ca2 3470/* Section Cell {{{ */
f641a0e5 3471@interface SectionCell : UISimpleTableCell {
6d9712c4 3472 NSString *section_;
f641a0e5
JF
3473 NSString *name_;
3474 NSString *count_;
3475 UIImage *icon_;
a9543575 3476 _UISwitchSlider *switch_;
6d9712c4 3477 BOOL editing_;
b4d89997 3478}
b19871dd 3479
36bb2ca2 3480- (id) init;
6d9712c4 3481- (void) setSection:(Section *)section editing:(BOOL)editing;
36bb2ca2 3482
8da60fb7
JF
3483@end
3484
36bb2ca2 3485@implementation SectionCell
8da60fb7 3486
f641a0e5
JF
3487- (void) clearSection {
3488 if (section_ != nil) {
6d9712c4 3489 [section_ release];
f641a0e5
JF
3490 section_ = nil;
3491 }
7b0ce2da 3492
f641a0e5
JF
3493 if (name_ != nil) {
3494 [name_ release];
3495 name_ = nil;
3496 }
7b0ce2da 3497
f641a0e5
JF
3498 if (count_ != nil) {
3499 [count_ release];
3500 count_ = nil;
3501 }
7b0ce2da
JF
3502}
3503
f641a0e5
JF
3504- (void) dealloc {
3505 [self clearSection];
3506 [icon_ release];
3507 [switch_ release];
3508 [super dealloc];
7b0ce2da
JF
3509}
3510
36bb2ca2 3511- (id) init {
b4d89997 3512 if ((self = [super init]) != nil) {
f641a0e5 3513 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
36bb2ca2 3514
a9543575 3515 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
6d9712c4 3516 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
b4d89997 3517 } return self;
b19871dd
JF
3518}
3519
6d9712c4
JF
3520- (void) onSwitch:(id)sender {
3521 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3522 if (metadata == nil) {
3523 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3524 [Sections_ setObject:metadata forKey:section_];
3525 }
3526
3527 Changed_ = true;
3528 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3529}
3530
3531- (void) setSection:(Section *)section editing:(BOOL)editing {
3532 if (editing != editing_) {
3533 if (editing_)
3534 [switch_ removeFromSuperview];
3535 else
3536 [self addSubview:switch_];
3537 editing_ = editing;
3538 }
3539
f641a0e5 3540 [self clearSection];
6d9712c4 3541
36bb2ca2 3542 if (section == nil) {
f641a0e5
JF
3543 name_ = [@"All Packages" retain];
3544 count_ = nil;
36bb2ca2 3545 } else {
6d9712c4
JF
3546 section_ = [section name];
3547 if (section_ != nil)
3548 section_ = [section_ retain];
f641a0e5
JF
3549 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3550 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
6d9712c4
JF
3551
3552 if (editing_)
807ae6d7 3553 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
36bb2ca2 3554 }
f641a0e5
JF
3555}
3556
3557- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3558 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3559
3560 if (selected)
3561 UISetColor(White_);
3562
3563 if (!selected)
3564 UISetColor(Black_);
3565 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
6d9712c4 3566
f641a0e5
JF
3567 CGSize size = [count_ sizeWithFont:Font14_];
3568
3569 UISetColor(White_);
3570 if (count_ != nil)
c390d3ab 3571 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
f641a0e5
JF
3572
3573 [super drawContentInRect:rect selected:selected];
b19871dd
JF
3574}
3575
36bb2ca2
JF
3576@end
3577/* }}} */
b19871dd 3578
ab398adf
JF
3579/* File Table {{{ */
3580@interface FileTable : RVPage {
36bb2ca2 3581 _transient Database *database_;
ab398adf
JF
3582 Package *package_;
3583 NSString *name_;
3584 NSMutableArray *files_;
3585 UITable *list_;
3586}
b19871dd 3587
ab398adf
JF
3588- (id) initWithBook:(RVBook *)book database:(Database *)database;
3589- (void) setPackage:(Package *)package;
3590
3591@end
3592
3593@implementation FileTable
3594
3595- (void) dealloc {
3596 if (package_ != nil)
3597 [package_ release];
3598 if (name_ != nil)
3599 [name_ release];
3600 [files_ release];
3601 [list_ release];
3602 [super dealloc];
3603}
3604
3605- (int) numberOfRowsInTable:(UITable *)table {
3606 return files_ == nil ? 0 : [files_ count];
3607}
3608
3609- (float) table:(UITable *)table heightForRow:(int)row {
3610 return 24;
3611}
3612
3613- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3614 if (reusing == nil) {
3615 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
a9543575 3616 UIFont *font = [UIFont systemFontOfSize:16];
ab398adf 3617 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
ab398adf
JF
3618 }
3619 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3620 return reusing;
36bb2ca2 3621}
b19871dd 3622
58f6daef 3623- (BOOL) table:(UITable *)table canSelectRow:(int)row {
ab398adf
JF
3624 return NO;
3625}
b19871dd 3626
ab398adf
JF
3627- (id) initWithBook:(RVBook *)book database:(Database *)database {
3628 if ((self = [super initWithBook:book]) != nil) {
3629 database_ = database;
b19871dd 3630
ab398adf 3631 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
8da60fb7 3632
ab398adf
JF
3633 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3634 [self addSubview:list_];
3635
3636 UITableColumn *column = [[[UITableColumn alloc]
3637 initWithTitle:@"Name"
3638 identifier:@"name"
3639 width:[self frame].size.width
3640 ] autorelease];
3641
3642 [list_ setDataSource:self];
3643 [list_ setSeparatorStyle:1];
3644 [list_ addTableColumn:column];
3645 [list_ setDelegate:self];
3646 [list_ setReusesTableCells:YES];
3647 } return self;
3648}
3649
3650- (void) setPackage:(Package *)package {
3651 if (package_ != nil) {
3652 [package_ autorelease];
3653 package_ = nil;
3654 }
3655
3656 if (name_ != nil) {
3657 [name_ release];
3658 name_ = nil;
3659 }
3660
3661 [files_ removeAllObjects];
3662
3663 if (package != nil) {
3664 package_ = [package retain];
3665 name_ = [[package id] retain];
3666
affeffc7
JF
3667 if (NSArray *files = [package files])
3668 [files_ addObjectsFromArray:files];
ab398adf 3669
9ea8d159
JF
3670 if ([files_ count] != 0) {
3671 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3672 [files_ removeObjectAtIndex:0];
a54b1c10 3673 [files_ sortUsingSelector:@selector(compareByPath:)];
2388b078
JF
3674
3675 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3676 [stack addObject:@"/"];
3677
3678 for (int i(0), e([files_ count]); i != e; ++i) {
3679 NSString *file = [files_ objectAtIndex:i];
3680 while (![file hasPrefix:[stack lastObject]])
3681 [stack removeLastObject];
3682 NSString *directory = [stack lastObject];
3683 [stack addObject:[file stringByAppendingString:@"/"]];
3684 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
a54b1c10 3685 ([stack count] - 2) * 3, "",
2388b078
JF
3686 [file substringFromIndex:[directory length]]
3687 ]];
3688 }
ab398adf
JF
3689 }
3690 }
3691
3692 [list_ reloadData];
3693}
3694
3695- (void) resetViewAnimated:(BOOL)animated {
3696 [list_ resetViewAnimated:animated];
3697}
3698
3699- (void) reloadData {
3700 [self setPackage:[database_ packageWithName:name_]];
3701 [self reloadButtons];
3702}
3703
3704- (NSString *) title {
2388b078
JF
3705 return @"Installed Files";
3706}
3707
3708- (NSString *) backButtonTitle {
3709 return @"Files";
ab398adf 3710}
b4d89997 3711
b4d89997 3712@end
36bb2ca2 3713/* }}} */
36bb2ca2 3714/* Package View {{{ */
6f1a15d9 3715@interface PackageView : BrowserView {
770f2a8e 3716 _transient Database *database_;
36bb2ca2
JF
3717 Package *package_;
3718 NSString *name_;
5a09ae08 3719 NSMutableArray *buttons_;
b31b87cc
JF
3720}
3721
36bb2ca2
JF
3722- (id) initWithBook:(RVBook *)book database:(Database *)database;
3723- (void) setPackage:(Package *)package;
b4d89997 3724
36bb2ca2 3725@end
b4d89997 3726
36bb2ca2 3727@implementation PackageView
b4d89997 3728
36bb2ca2 3729- (void) dealloc {
36bb2ca2
JF
3730 if (package_ != nil)
3731 [package_ release];
3732 if (name_ != nil)
3733 [name_ release];
5a09ae08 3734 [buttons_ release];
36bb2ca2
JF
3735 [super dealloc];
3736}
8da60fb7 3737
5a09ae08
JF
3738- (void) _clickButtonWithName:(NSString *)name {
3739 if ([name isEqualToString:@"Install"])
3740 [delegate_ installPackage:package_];
3741 else if ([name isEqualToString:@"Reinstall"])
3742 [delegate_ installPackage:package_];
3743 else if ([name isEqualToString:@"Remove"])
3744 [delegate_ removePackage:package_];
3745 else if ([name isEqualToString:@"Upgrade"])
3746 [delegate_ installPackage:package_];
3747 else _assert(false);
3748}
3749
a9543575 3750- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5a09ae08
JF
3751 int count = [buttons_ count];
3752 _assert(count != 0);
3753 _assert(button <= count + 1);
3754
3755 if (count != button - 1)
3756 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
36bb2ca2
JF
3757
3758 [sheet dismiss];
b4d89997 3759}
2367a917 3760
7d2ac47f
JF
3761- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3762 [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
3763 return [super webView:sender didFinishLoadForFrame:frame];
3764}
3765
6f1a15d9 3766- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6f1a15d9 3767 [window setValue:package_ forKey:@"package"];
c390d3ab 3768 [super webView:sender didClearWindowObject:window forFrame:frame];
6f1a15d9
JF
3769}
3770
36bb2ca2 3771- (void) _rightButtonClicked {
6f1a15d9
JF
3772 /*[super _rightButtonClicked];
3773 return;*/
3774
5a09ae08
JF
3775 int count = [buttons_ count];
3776 _assert(count != 0);
2367a917 3777
5a09ae08
JF
3778 if (count == 1)
3779 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3780 else {
3781 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3782 [buttons addObjectsFromArray:buttons_];
36bb2ca2
JF
3783 [buttons addObject:@"Cancel"];
3784
a9543575 3785 [delegate_ slideUp:[[[UIActionSheet alloc]
9ea8d159 3786 initWithTitle:nil
36bb2ca2
JF
3787 buttons:buttons
3788 defaultButtonIndex:2
3789 delegate:self
a54b1c10 3790 context:@"manage"
36bb2ca2
JF
3791 ] autorelease]];
3792 }
b4d89997 3793}
2367a917 3794
6f1a15d9 3795- (NSString *) _rightButtonTitle {
5a09ae08
JF
3796 int count = [buttons_ count];
3797 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
b4d89997 3798}
2367a917 3799
affeffc7 3800- (NSString *) backButtonTitle {
36bb2ca2 3801 return @"Details";
dc5812ec
JF
3802}
3803
36bb2ca2 3804- (id) initWithBook:(RVBook *)book database:(Database *)database {
770f2a8e 3805 if ((self = [super initWithBook:book]) != nil) {
36bb2ca2 3806 database_ = database;
5a09ae08 3807 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
36bb2ca2 3808 } return self;
dc5812ec
JF
3809}
3810
36bb2ca2
JF
3811- (void) setPackage:(Package *)package {
3812 if (package_ != nil) {
3813 [package_ autorelease];
3814 package_ = nil;
3815 }
3816
3817 if (name_ != nil) {
3818 [name_ release];
3819 name_ = nil;
3820 }
3821
5a09ae08
JF
3822 [buttons_ removeAllObjects];
3823
36bb2ca2
JF
3824 if (package != nil) {
3825 package_ = [package retain];
3826 name_ = [[package id] retain];
3827
6f1a15d9 3828 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
5a09ae08
JF
3829
3830 if ([package_ source] == nil);
31f3cfff
JF
3831 else if ([package_ upgradableAndEssential:NO])
3832 [buttons_ addObject:@"Upgrade"];
5a09ae08
JF
3833 else if ([package_ installed] == nil)
3834 [buttons_ addObject:@"Install"];
5a09ae08
JF
3835 else
3836 [buttons_ addObject:@"Reinstall"];
3837 if ([package_ installed] != nil)
3838 [buttons_ addObject:@"Remove"];
36bb2ca2 3839 }
dc5812ec
JF
3840}
3841
36bb2ca2
JF
3842- (void) reloadData {
3843 [self setPackage:[database_ packageWithName:name_]];
3844 [self reloadButtons];
8fe19fc1
JF
3845}
3846
b4d89997
JF
3847@end
3848/* }}} */
b4d89997 3849/* Package Table {{{ */
36bb2ca2
JF
3850@interface PackageTable : RVPage {
3851 _transient Database *database_;
3852 NSString *title_;
3853 SEL filter_;
3854 id object_;
3855 NSMutableArray *packages_;
b4d89997 3856 NSMutableArray *sections_;
36bb2ca2 3857 UISectionList *list_;
dc5812ec
JF
3858}
3859
36bb2ca2 3860- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
b4d89997
JF
3861
3862- (void) setDelegate:(id)delegate;
a0be02eb
JF
3863- (void) setObject:(id)object;
3864
36bb2ca2 3865- (void) reloadData;
a0be02eb 3866- (void) resetCursor;
b4d89997 3867
2388b078
JF
3868- (UISectionList *) list;
3869
ab398adf
JF
3870- (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3871
b4d89997
JF
3872@end
3873
3874@implementation PackageTable
3875
3876- (void) dealloc {
36bb2ca2
JF
3877 [list_ setDataSource:nil];
3878
3879 [title_ release];
3880 if (object_ != nil)
3881 [object_ release];
3882 [packages_ release];
b4d89997 3883 [sections_ release];
36bb2ca2 3884 [list_ release];
b4d89997 3885 [super dealloc];
8fe19fc1
JF
3886}
3887
b4d89997
JF
3888- (int) numberOfSectionsInSectionList:(UISectionList *)list {
3889 return [sections_ count];
3890}
2367a917 3891
b4d89997
JF
3892- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3893 return [[sections_ objectAtIndex:section] name];
3894}
dc5812ec 3895
b4d89997
JF
3896- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3897 return [[sections_ objectAtIndex:section] row];
2367a917
JF
3898}
3899
b4d89997
JF
3900- (int) numberOfRowsInTable:(UITable *)table {
3901 return [packages_ count];
3902}
dc5812ec 3903
b4d89997 3904- (float) table:(UITable *)table heightForRow:(int)row {
9bedffaa 3905 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
b4d89997 3906}
dc5812ec 3907
b4d89997
JF
3908- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3909 if (reusing == nil)
36bb2ca2 3910 reusing = [[[PackageCell alloc] init] autorelease];
b4d89997
JF
3911 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3912 return reusing;
3913}
dc5812ec 3914
b4d89997
JF
3915- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3916 return NO;
3917}
dc5812ec 3918
b4d89997
JF
3919- (void) tableRowSelected:(NSNotification *)notification {
3920 int row = [[notification object] selectedRow];
36bb2ca2
JF
3921 if (row == INT_MAX)
3922 return;
3923
3924 Package *package = [packages_ objectAtIndex:row];
3925 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3926 [view setDelegate:delegate_];
3927 [view setPackage:package];
3928 [book_ pushPage:view];
dc5812ec
JF
3929}
3930
36bb2ca2
JF
3931- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3932 if ((self = [super initWithBook:book]) != nil) {
3933 database_ = database;
3934 title_ = [title retain];
3935 filter_ = filter;
3936 object_ = object == nil ? nil : [object retain];
3937
3938 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
b4d89997 3939 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 3940
b4d89997
JF
3941 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3942 [list_ setDataSource:self];
dc5812ec 3943
b4d89997
JF
3944 UITableColumn *column = [[[UITableColumn alloc]
3945 initWithTitle:@"Name"
3946 identifier:@"name"
36bb2ca2 3947 width:[self frame].size.width
b4d89997
JF
3948 ] autorelease];
3949
3950 UITable *table = [list_ table];
3951 [table setSeparatorStyle:1];
3952 [table addTableColumn:column];
3953 [table setDelegate:self];
3954 [table setReusesTableCells:YES];
3955
3956 [self addSubview:list_];
36bb2ca2 3957 [self reloadData];
baf80942
JF
3958
3959 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
3960 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
b4d89997 3961 } return self;
dc5812ec
JF
3962}
3963
3964- (void) setDelegate:(id)delegate {
2367a917 3965 delegate_ = delegate;
b4d89997
JF
3966}
3967
a0be02eb
JF
3968- (void) setObject:(id)object {
3969 if (object_ != nil)
3970 [object_ release];
3971 if (object == nil)
3972 object_ = nil;
3973 else
3974 object_ = [object retain];
3975}
3976
36bb2ca2
JF
3977- (void) reloadData {
3978 NSArray *packages = [database_ packages];
b4d89997 3979
36bb2ca2 3980 [packages_ removeAllObjects];
b4d89997
JF
3981 [sections_ removeAllObjects];
3982
36bb2ca2
JF
3983 for (size_t i(0); i != [packages count]; ++i) {
3984 Package *package([packages objectAtIndex:i]);
7d2ac47f 3985 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
36bb2ca2
JF
3986 [packages_ addObject:package];
3987 }
3988
b4d89997
JF
3989 Section *section = nil;
3990
3991 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3992 Package *package = [packages_ objectAtIndex:offset];
3993 NSString *name = [package index];
3994
36bb2ca2 3995 if (section == nil || ![[section name] isEqualToString:name]) {
b4d89997
JF
3996 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3997 [sections_ addObject:section];
3998 }
3999
36bb2ca2 4000 [section addToCount];
b4d89997
JF
4001 }
4002
4003 [list_ reloadData];
4004}
4005
36bb2ca2
JF
4006- (NSString *) title {
4007 return title_;
b4d89997
JF
4008}
4009
36bb2ca2
JF
4010- (void) resetViewAnimated:(BOOL)animated {
4011 [list_ resetViewAnimated:animated];
dc5812ec
JF
4012}
4013
a0be02eb
JF
4014- (void) resetCursor {
4015 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4016}
4017
2388b078
JF
4018- (UISectionList *) list {
4019 return list_;
4020}
4021
ab398adf
JF
4022- (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4023 [list_ setShouldHideHeaderInShortLists:hide];
4024}
4025
dc5812ec 4026@end
b4d89997 4027/* }}} */
dc5812ec 4028
7b0ce2da
JF
4029/* Add Source View {{{ */
4030@interface AddSourceView : RVPage {
4031 _transient Database *database_;
dc5812ec
JF
4032}
4033
7b0ce2da 4034- (id) initWithBook:(RVBook *)book database:(Database *)database;
b4d89997 4035
7b0ce2da 4036@end
dc5812ec 4037
7b0ce2da 4038@implementation AddSourceView
dc5812ec 4039
7b0ce2da
JF
4040- (id) initWithBook:(RVBook *)book database:(Database *)database {
4041 if ((self = [super initWithBook:book]) != nil) {
4042 database_ = database;
4043 } return self;
36bb2ca2 4044}
dc5812ec 4045
7b0ce2da
JF
4046@end
4047/* }}} */
4048/* Source Cell {{{ */
4049@interface SourceCell : UITableCell {
f641a0e5
JF
4050 UIImage *icon_;
4051 NSString *origin_;
4052 NSString *description_;
4053 NSString *label_;
b4d89997
JF
4054}
4055
7b0ce2da 4056- (void) dealloc;
b4d89997 4057
7b0ce2da 4058- (SourceCell *) initWithSource:(Source *)source;
b4d89997 4059
7b0ce2da 4060@end
b4d89997 4061
7b0ce2da 4062@implementation SourceCell
b4d89997 4063
7b0ce2da 4064- (void) dealloc {
f641a0e5 4065 [icon_ release];
7b0ce2da
JF
4066 [origin_ release];
4067 [description_ release];
4068 [label_ release];
4069 [super dealloc];
36bb2ca2 4070}
b4d89997 4071
7b0ce2da
JF
4072- (SourceCell *) initWithSource:(Source *)source {
4073 if ((self = [super init]) != nil) {
f641a0e5
JF
4074 if (icon_ == nil)
4075 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4076 if (icon_ == nil)
4077 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4078 icon_ = [icon_ retain];
4079
4080 origin_ = [[source name] retain];
4081 label_ = [[source uri] retain];
4082 description_ = [[source description] retain];
7b0ce2da 4083 } return self;
a0376fc1
JF
4084}
4085
f641a0e5
JF
4086- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4087 if (icon_ != nil)
4088 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
7b0ce2da 4089
f641a0e5
JF
4090 if (selected)
4091 UISetColor(White_);
7b0ce2da 4092
f641a0e5
JF
4093 if (!selected)
4094 UISetColor(Black_);
4095 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
7b0ce2da 4096
f641a0e5
JF
4097 if (!selected)
4098 UISetColor(Blue_);
4099 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
7b0ce2da 4100
f641a0e5
JF
4101 if (!selected)
4102 UISetColor(Gray_);
4103 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
7b0ce2da 4104
f641a0e5 4105 [super drawContentInRect:rect selected:selected];
7b0ce2da
JF
4106}
4107
4108@end
4109/* }}} */
4110/* Source Table {{{ */
4111@interface SourceTable : RVPage {
4112 _transient Database *database_;
4113 UISectionList *list_;
4114 NSMutableArray *sources_;
a9543575 4115 UIActionSheet *alert_;
7b0ce2da
JF
4116 int offset_;
4117
4118 NSString *href_;
4119 UIProgressHUD *hud_;
4120 NSError *error_;
4121
4122 //NSURLConnection *installer_;
4123 NSURLConnection *trivial_bz2_;
4124 NSURLConnection *trivial_gz_;
4125 //NSURLConnection *automatic_;
4126
4127 BOOL trivial_;
4128}
4129
4130- (id) initWithBook:(RVBook *)book database:(Database *)database;
4131
4132@end
4133
4134@implementation SourceTable
4135
4136- (void) _deallocConnection:(NSURLConnection *)connection {
4137 if (connection != nil) {
4138 [connection cancel];
4139 //[connection setDelegate:nil];
4140 [connection release];
4141 }
4142}
4143
4144- (void) dealloc {
4145 [[list_ table] setDelegate:nil];
4146 [list_ setDataSource:nil];
4147
4148 if (href_ != nil)
4149 [href_ release];
4150 if (hud_ != nil)
4151 [hud_ release];
4152 if (error_ != nil)
4153 [error_ release];
4154
4155 //[self _deallocConnection:installer_];
4156 [self _deallocConnection:trivial_gz_];
4157 [self _deallocConnection:trivial_bz2_];
4158 //[self _deallocConnection:automatic_];
4159
4160 [sources_ release];
4161 [list_ release];
4162 [super dealloc];
4163}
4164
4165- (int) numberOfSectionsInSectionList:(UISectionList *)list {
4166 return offset_ == 0 ? 1 : 2;
4167}
4168
4169- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4170 switch (section + (offset_ == 0 ? 1 : 0)) {
4171 case 0: return @"Entered by User";
4172 case 1: return @"Installed by Packages";
4173
4174 default:
4175 _assert(false);
4176 return nil;
4177 }
4178}
4179
4180- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4181 switch (section + (offset_ == 0 ? 1 : 0)) {
4182 case 0: return 0;
4183 case 1: return offset_;
4184
4185 default:
4186 _assert(false);
4187 return -1;
4188 }
4189}
4190
4191- (int) numberOfRowsInTable:(UITable *)table {
4192 return [sources_ count];
4193}
4194
4195- (float) table:(UITable *)table heightForRow:(int)row {
4196 Source *source = [sources_ objectAtIndex:row];
f641a0e5 4197 return [source description] == nil ? 56 : 73;
7b0ce2da
JF
4198}
4199
4200- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4201 Source *source = [sources_ objectAtIndex:row];
4202 // XXX: weird warning, stupid selectors ;P
4203 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4204}
4205
4206- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4207 return YES;
4208}
4209
4210- (BOOL) table:(UITable *)table canSelectRow:(int)row {
4211 return YES;
4212}
4213
4214- (void) tableRowSelected:(NSNotification*)notification {
4215 UITable *table([list_ table]);
4216 int row([table selectedRow]);
4217 if (row == INT_MAX)
4218 return;
4219
4220 Source *source = [sources_ objectAtIndex:row];
4221
4222 PackageTable *packages = [[[PackageTable alloc]
4223 initWithBook:book_
4224 database:database_
4225 title:[source label]
4226 filter:@selector(isVisibleInSource:)
4227 with:source
4228 ] autorelease];
4229
4230 [packages setDelegate:delegate_];
4231
4232 [book_ pushPage:packages];
4233}
4234
4235- (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4236 Source *source = [sources_ objectAtIndex:row];
4237 return [source record] != nil;
4238}
4239
4240- (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4241 [[list_ table] setDeleteConfirmationRow:row];
4242}
4243
4244- (void) table:(UITable *)table deleteRow:(int)row {
4245 Source *source = [sources_ objectAtIndex:row];
4246 [Sources_ removeObjectForKey:[source key]];
4247 [delegate_ syncData];
4248}
4249
4250- (void) _endConnection:(NSURLConnection *)connection {
4251 NSURLConnection **field = NULL;
4252 if (connection == trivial_bz2_)
4253 field = &trivial_bz2_;
4254 else if (connection == trivial_gz_)
4255 field = &trivial_gz_;
4256 _assert(field != NULL);
4257 [connection release];
4258 *field = nil;
4259
4260 if (
4261 trivial_bz2_ == nil &&
4262 trivial_gz_ == nil
4263 ) {
4264 [delegate_ setStatusBarShowsProgress:NO];
4265
4266 [hud_ show:NO];
4267 [hud_ removeFromSuperview];
4268 [hud_ autorelease];
4269 hud_ = nil;
4270
4271 if (trivial_) {
4272 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4273 @"deb", @"Type",
4274 href_, @"URI",
4275 @"./", @"Distribution",
4276 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4277
4278 [delegate_ syncData];
4279 } else if (error_ != nil) {
a9543575 4280 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4281 initWithTitle:@"Verification Error"
4282 buttons:[NSArray arrayWithObjects:@"OK", nil]
4283 defaultButtonIndex:0
4284 delegate:self
4285 context:@"urlerror"
4286 ] autorelease];
4287
4288 [sheet setBodyText:[error_ localizedDescription]];
4289 [sheet popupAlertAnimated:YES];
4290 } else {
a9543575 4291 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4292 initWithTitle:@"Did not Find Repository"
4293 buttons:[NSArray arrayWithObjects:@"OK", nil]
4294 defaultButtonIndex:0
4295 delegate:self
4296 context:@"trivial"
4297 ] autorelease];
4298
4299 [sheet setBodyText:@"The indicated repository could not be found. This could be because you are trying to add a legacy Installer repository (these are not supported). Also, this interface is only capable of working with exact repository URLs. If you host a repository and are having issues please contact the author of Cydia with any questions you have."];
4300 [sheet popupAlertAnimated:YES];
4301 }
4302
4303 [href_ release];
4304 href_ = nil;
4305
4306 if (error_ != nil) {
4307 [error_ release];
4308 error_ = nil;
4309 }
4310 }
4311}
4312
4313- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4314 switch ([response statusCode]) {
4315 case 200:
4316 trivial_ = YES;
4317 }
4318}
4319
4320- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
c390d3ab 4321 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
7b0ce2da
JF
4322 if (error_ != nil)
4323 error_ = [error retain];
4324 [self _endConnection:connection];
4325}
4326
4327- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4328 [self _endConnection:connection];
4329}
4330
4331- (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4332 NSMutableURLRequest *request = [NSMutableURLRequest
4333 requestWithURL:[NSURL URLWithString:href]
4334 cachePolicy:NSURLRequestUseProtocolCachePolicy
4335 timeoutInterval:20.0
4336 ];
4337
4338 [request setHTTPMethod:method];
4339
4340 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4341}
4342
a9543575 4343- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7b0ce2da
JF
4344 NSString *context = [sheet context];
4345 if ([context isEqualToString:@"source"])
4346 switch (button) {
4347 case 1: {
4348 NSString *href = [[sheet textField] text];
4349
4350 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4351
4352 if (![href hasSuffix:@"/"])
4353 href_ = [href stringByAppendingString:@"/"];
4354 else
4355 href_ = href;
4356 href_ = [href_ retain];
4357
4358 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4359 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4360 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4361
4362 trivial_ = false;
4363
4364 hud_ = [delegate_ addProgressHUD];
4365 [hud_ setText:@"Verifying URL"];
4366 } break;
4367
4368 case 2:
4369 break;
4370
4371 default:
4372 _assert(false);
4373 }
4374
4375 [sheet dismiss];
4376}
4377
4378- (id) initWithBook:(RVBook *)book database:(Database *)database {
4379 if ((self = [super initWithBook:book]) != nil) {
4380 database_ = database;
4381 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4382
4383 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4384 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4385 [list_ setShouldHideHeaderInShortLists:NO];
4386
4387 [self addSubview:list_];
4388 [list_ setDataSource:self];
4389
4390 UITableColumn *column = [[UITableColumn alloc]
4391 initWithTitle:@"Name"
4392 identifier:@"name"
4393 width:[self frame].size.width
4394 ];
4395
4396 UITable *table = [list_ table];
4397 [table setSeparatorStyle:1];
4398 [table addTableColumn:column];
4399 [table setDelegate:self];
4400
4401 [self reloadData];
baf80942
JF
4402
4403 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4404 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
7b0ce2da
JF
4405 } return self;
4406}
4407
4408- (void) reloadData {
4409 pkgSourceList list;
4410 _assert(list.ReadMainList());
4411
4412 [sources_ removeAllObjects];
4413 [sources_ addObjectsFromArray:[database_ sources]];
807ae6d7 4414 _trace();
7b0ce2da 4415 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
807ae6d7 4416 _trace();
7b0ce2da
JF
4417
4418 int count = [sources_ count];
4419 for (offset_ = 0; offset_ != count; ++offset_) {
4420 Source *source = [sources_ objectAtIndex:offset_];
4421 if ([source record] == nil)
4422 break;
4423 }
4424
4425 [list_ reloadData];
4426}
4427
4428- (void) resetViewAnimated:(BOOL)animated {
4429 [list_ resetViewAnimated:animated];
4430}
4431
4432- (void) _leftButtonClicked {
4433 /*[book_ pushPage:[[[AddSourceView alloc]
4434 initWithBook:book_
4435 database:database_
4436 ] autorelease]];*/
4437
a9543575 4438 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4439 initWithTitle:@"Enter Cydia/APT URL"
4440 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4441 defaultButtonIndex:0
4442 delegate:self
4443 context:@"source"
4444 ] autorelease];
4445
4446 [sheet addTextFieldWithValue:@"http://" label:@""];
4447
a9543575
JF
4448 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4449 [traits setAutocapitalizationType:0];
4450 [traits setKeyboardType:3];
4451 [traits setAutocorrectionType:1];
7b0ce2da
JF
4452
4453 [sheet popupAlertAnimated:YES];
4454}
4455
4456- (void) _rightButtonClicked {
4457 UITable *table = [list_ table];
4458 BOOL editing = [table isRowDeletionEnabled];
4459 [table enableRowDeletion:!editing animated:YES];
4460 [book_ reloadButtonsForPage:self];
4461}
4462
4463- (NSString *) title {
4464 return @"Sources";
4465}
4466
7b0ce2da
JF
4467- (NSString *) leftButtonTitle {
4468 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4469}
4470
4471- (NSString *) rightButtonTitle {
4472 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4473}
4474
baf80942
JF
4475- (UINavigationButtonStyle) rightButtonStyle {
4476 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
7b0ce2da
JF
4477}
4478
4479@end
4480/* }}} */
4481
4482/* Installed View {{{ */
4483@interface InstalledView : RVPage {
4484 _transient Database *database_;
4485 PackageTable *packages_;
f641a0e5 4486 BOOL expert_;
7b0ce2da
JF
4487}
4488
4489- (id) initWithBook:(RVBook *)book database:(Database *)database;
4490
4491@end
4492
4493@implementation InstalledView
4494
4495- (void) dealloc {
4496 [packages_ release];
4497 [super dealloc];
4498}
4499
4500- (id) initWithBook:(RVBook *)book database:(Database *)database {
4501 if ((self = [super initWithBook:book]) != nil) {
4502 database_ = database;
4503
4504 packages_ = [[PackageTable alloc]
4505 initWithBook:book
4506 database:database
4507 title:nil
440c215d 4508 filter:@selector(isInstalledAndVisible:)
f641a0e5 4509 with:[NSNumber numberWithBool:YES]
7b0ce2da
JF
4510 ];
4511
4512 [self addSubview:packages_];
baf80942
JF
4513
4514 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4515 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
7b0ce2da
JF
4516 } return self;
4517}
4518
4519- (void) resetViewAnimated:(BOOL)animated {
4520 [packages_ resetViewAnimated:animated];
4521}
4522
4523- (void) reloadData {
4524 [packages_ reloadData];
4525}
4526
f641a0e5
JF
4527- (void) _rightButtonClicked {
4528 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4529 [packages_ reloadData];
4530 expert_ = !expert_;
4531 [book_ reloadButtonsForPage:self];
4532}
4533
7b0ce2da 4534- (NSString *) title {
f641a0e5 4535 return @"Installed";
7b0ce2da
JF
4536}
4537
4538- (NSString *) backButtonTitle {
4539 return @"Packages";
4540}
4541
f641a0e5
JF
4542- (NSString *) rightButtonTitle {
4543 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4544}
4545
baf80942
JF
4546- (UINavigationButtonStyle) rightButtonStyle {
4547 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
f641a0e5
JF
4548}
4549
7b0ce2da
JF
4550- (void) setDelegate:(id)delegate {
4551 [super setDelegate:delegate];
4552 [packages_ setDelegate:delegate];
4553}
4554
4555@end
4556/* }}} */
4557
98228790 4558/* Home View {{{ */
7b0ce2da
JF
4559@interface HomeView : BrowserView {
4560}
4561
4562@end
4563
4564@implementation HomeView
4565
a9543575 4566- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7b0ce2da
JF
4567 [sheet dismiss];
4568}
4569
4570- (void) _leftButtonClicked {
a9543575 4571 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4572 initWithTitle:@"About Cydia Installer"
4573 buttons:[NSArray arrayWithObjects:@"Close", nil]
4574 defaultButtonIndex:0
4575 delegate:self
4576 context:@"about"
4577 ] autorelease];
4578
4579 [sheet setBodyText:
4580 @"Copyright (C) 2008\n"
4581 "Jay Freeman (saurik)\n"
4582 "saurik@saurik.com\n"
4583 "http://www.saurik.com/\n"
4584 "\n"
4585 "The Okori Group\n"
4586 "http://www.theokorigroup.com/\n"
4587 "\n"
4588 "College of Creative Studies,\n"
4589 "University of California,\n"
4590 "Santa Barbara\n"
4591 "http://www.ccs.ucsb.edu/"
4592 ];
4593
4594 [sheet popupAlertAnimated:YES];
4595}
4596
4597- (NSString *) leftButtonTitle {
4598 return @"About";
4599}
4600
4601@end
98228790
JF
4602/* }}} */
4603/* Manage View {{{ */
7b0ce2da
JF
4604@interface ManageView : BrowserView {
4605}
4606
4607@end
4608
4609@implementation ManageView
4610
4611- (NSString *) title {
4612 return @"Manage";
4613}
4614
4615- (void) _leftButtonClicked {
4616 [delegate_ askForSettings];
4617}
4618
4619- (NSString *) leftButtonTitle {
4620 return @"Settings";
4621}
4622
6f1a15d9 4623- (NSString *) _rightButtonTitle {
7b0ce2da
JF
4624 return nil;
4625}
4626
4627@end
98228790 4628/* }}} */
7b0ce2da 4629
c390d3ab
JF
4630@interface WebView (Cydia)
4631- (void) setScriptDebugDelegate:(id)delegate;
4632- (void) _setFormDelegate:(id)delegate;
4633- (void) _setUIKitDelegate:(id)delegate;
4634- (void) setWebMailDelegate:(id)delegate;
4635- (void) _setLayoutInterval:(float)interval;
4636@end
4637
baf80942
JF
4638/* Indirect Delegate {{{ */
4639@interface IndirectDelegate : NSProxy {
c390d3ab 4640 _transient volatile id delegate_;
baf80942
JF
4641}
4642
4643- (void) setDelegate:(id)delegate;
4644- (id) initWithDelegate:(id)delegate;
4645@end
4646
4647@implementation IndirectDelegate
4648
4649- (void) setDelegate:(id)delegate {
4650 delegate_ = delegate;
4651}
4652
4653- (id) initWithDelegate:(id)delegate {
4654 delegate_ = delegate;
4655 return self;
4656}
4657
4658- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4659 if (delegate_ != nil)
4660 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4661 return sig;
c390d3ab
JF
4662 // XXX: I fucking hate Apple so very very bad
4663 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
baf80942
JF
4664}
4665
c390d3ab 4666- (void) forwardInvocation:(NSInvocation *)inv {
baf80942
JF
4667 SEL sel = [inv selector];
4668 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4669 [inv invokeWithTarget:delegate_];
4670}
4671
4672@end
4673/* }}} */
7b0ce2da
JF
4674/* Browser Implementation {{{ */
4675@implementation BrowserView
4676
4677- (void) dealloc {
4678 WebView *webview = [webview_ webView];
4679 [webview setFrameLoadDelegate:nil];
4680 [webview setResourceLoadDelegate:nil];
4681 [webview setUIDelegate:nil];
c390d3ab
JF
4682 [webview setScriptDebugDelegate:nil];
4683 [webview setPolicyDelegate:nil];
4684
4685 [webview setDownloadDelegate:nil];
4686
4687 [webview _setFormDelegate:nil];
4688 [webview _setUIKitDelegate:nil];
4689 [webview setWebMailDelegate:nil];
4690 [webview setEditingDelegate:nil];
7b0ce2da 4691
7b0ce2da 4692 [webview_ setDelegate:nil];
baf80942 4693 [webview_ setGestureDelegate:nil];
7b0ce2da 4694
c390d3ab
JF
4695 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4696
c390d3ab
JF
4697 [webview close];
4698
affeffc7
JF
4699#if RecycleWebViews
4700 [webview_ removeFromSuperview];
4701 [Documents_ addObject:[webview_ autorelease]];
4702#else
7b0ce2da 4703 [webview_ release];
affeffc7 4704#endif
baf80942
JF
4705
4706 [indirect_ setDelegate:nil];
4707 [indirect_ release];
4708
4709 [scroller_ setDelegate:nil];
4710
4711 [scroller_ release];
7b0ce2da
JF
4712 [urls_ release];
4713 [indicator_ release];
4714 if (title_ != nil)
4715 [title_ release];
4716 [super dealloc];
4717}
4718
4719- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4720 [self loadRequest:[NSURLRequest
4721 requestWithURL:url
4722 cachePolicy:policy
4723 timeoutInterval:30.0
4724 ]];
4725}
4726
4727- (void) loadURL:(NSURL *)url {
4728 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4729}
4730
4731- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4732 NSMutableURLRequest *copy = [request mutableCopy];
4733
c390d3ab
JF
4734 if (Machine_ != NULL)
4735 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4736 if (UniqueID_ != nil)
4737 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
7b0ce2da
JF
4738
4739 if (Role_ != nil)
4740 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4741
4742 return copy;
4743}
4744
4745- (void) loadRequest:(NSURLRequest *)request {
4746 pushed_ = true;
4747 [webview_ loadRequest:request];
4748}
4749
4750- (void) reloadURL {
87f46a96
JF
4751 if ([urls_ count] == 0)
4752 return;
7b0ce2da
JF
4753 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4754 [urls_ removeLastObject];
4755 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4756}
4757
4758- (WebView *) webView {
4759 return [webview_ webView];
4760}
4761
4762- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4763 [scroller_ setContentSize:frame.size];
4764}
4765
4766- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4767 [self view:sender didSetFrame:frame];
4768}
4769
4770- (void) pushPage:(RVPage *)page {
4771 [self setBackButtonTitle:title_];
4772 [page setDelegate:delegate_];
4773 [book_ pushPage:page];
4774}
4775
affeffc7
JF
4776- (BOOL) getSpecial:(NSURL *)url {
4777 NSString *href([url absoluteString]);
4778 NSString *scheme([[url scheme] lowercaseString]);
4779
7b0ce2da
JF
4780 RVPage *page = nil;
4781
affeffc7 4782 if ([href hasPrefix:@"apptapp://package/"])
c390d3ab 4783 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
770f2a8e 4784 else if ([scheme isEqualToString:@"cydia"]) {
affeffc7 4785 page = [delegate_ pageForURL:url hasTag:NULL];
770f2a8e
JF
4786 if (page == nil)
4787 return false;
4788 } else if (![scheme isEqualToString:@"apptapp"])
a9a0661e 4789 return false;
f641a0e5
JF
4790
4791 if (page != nil)
4792 [self pushPage:page];
a9a0661e 4793 return true;
7b0ce2da
JF
4794}
4795
c390d3ab
JF
4796- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4797 [window setValue:delegate_ forKey:@"cydia"];
4798}
4799
affeffc7
JF
4800- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4801 if (NSURL *url = [request URL]) {
4802 if (![self getSpecial:url]) {
4803 NSString *scheme([[url scheme] lowercaseString]);
4804 if ([scheme isEqualToString:@"mailto"])
4805 [delegate_ openMailToURL:url];
4806 else goto use;
4807 }
4808
4809 [listener ignore];
4810 } else use:
4811 [listener use];
4812}
4813
807ae6d7
JF
4814- (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4815 if ([WebView canShowMIMEType:type])
4816 [listener use];
4817 else {
4818 // XXX: handle more mime types!
4819 [listener ignore];
4820 if (frame == [webView mainFrame])
4821 [UIApp openURL:[request URL]];
4822 }
4823}
4824
affeffc7
JF
4825- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4826 NSURL *url([request URL]);
4827
4828 if (url == nil) use: {
4829 [listener use];
7b0ce2da 4830 return;
affeffc7
JF
4831 }
4832
4833 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4834
4835 if (
4836 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4837 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4838 ) {
4839 open:
4840 [UIApp openURL:url];
4841 ignore:
4842 [listener ignore];
7b0ce2da 4843 return;
affeffc7
JF
4844 }
4845
4846 int store(_not(int));
4847 if (NSURL *itms = [url itmsURL:&store]) {
807ae6d7 4848 NSLog(@"itms#%@#%u#%@", url, store, itms);
affeffc7
JF
4849 if (
4850 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4851 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4852 ) {
4853 url = itms;
4854 goto open;
4855 }
4856 }
4857
4858 NSString *scheme([[url scheme] lowercaseString]);
4859
4860 if ([scheme isEqualToString:@"tel"]) {
4861 // XXX: intelligence
4862 goto open;
4863 }
4864
4865 if ([scheme isEqualToString:@"mailto"]) {
4866 [delegate_ openMailToURL:url];
4867 goto ignore;
4868 }
4869
4870 if ([self getSpecial:url])
4871 goto ignore;
4872 else if ([WebView _canHandleRequest:request])
4873 goto use;
4874 else if ([url isSpringboardHandledURL])
4875 goto open;
4876 else
4877 goto use;
7b0ce2da
JF
4878}
4879
c390d3ab
JF
4880- (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4881 //lprintf("Status:%s\n", [text UTF8String]);
4882}
4883
4884- (void) _pushPage {
4885 if (pushed_)
4886 return;
4887 pushed_ = true;
4888 [book_ pushPage:self];
4889}
4890
f641a0e5
JF
4891- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4892 NSURL *url = [request URL];
affeffc7 4893 if ([self getSpecial:url])
7b0ce2da 4894 return nil;
c390d3ab 4895 [self _pushPage];
7b0ce2da
JF
4896 return [self _addHeadersToRequest:request];
4897}
4898
c390d3ab 4899- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
7b0ce2da
JF
4900 [self setBackButtonTitle:title_];
4901
770f2a8e 4902 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
7b0ce2da
JF
4903 [browser setDelegate:delegate_];
4904
c390d3ab 4905 if (pushed) {
7b0ce2da
JF
4906 [browser loadRequest:[self _addHeadersToRequest:request]];
4907 [book_ pushPage:browser];
4908 }
4909
4910 return [browser webView];
4911}
4912
c390d3ab
JF
4913- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4914 return [self _createWebViewWithRequest:request pushed:(request != nil)];
4915}
4916
4917- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
4918 return [self _createWebViewWithRequest:request pushed:YES];
4919}
4920
7b0ce2da 4921- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
25a2158d
JF
4922 if ([frame parentFrame] != nil)
4923 return;
4924
7b0ce2da 4925 title_ = [title retain];
affeffc7 4926 [book_ reloadTitleForPage:self];
7b0ce2da 4927}
36bb2ca2
JF
4928
4929- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4930 if ([frame parentFrame] != nil)
4931 return;
4932
1d3b229d 4933 reloading_ = false;
36bb2ca2
JF
4934 loading_ = true;
4935 [indicator_ startAnimation];
4936 [self reloadButtons];
4937
4938 if (title_ != nil) {
4939 [title_ release];
4940 title_ = nil;
b4d89997 4941 }
36bb2ca2 4942
affeffc7 4943 [book_ reloadTitleForPage:self];
36bb2ca2
JF
4944
4945 WebView *webview = [webview_ webView];
4946 NSString *href = [webview mainFrameURL];
4947 [urls_ addObject:[NSURL URLWithString:href]];
4948
c390d3ab
JF
4949 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
4950
7d2ac47f 4951 CGRect webrect = [scroller_ bounds];
36bb2ca2
JF
4952 webrect.size.height = 0;
4953 [webview_ setFrame:webrect];
dc5812ec
JF
4954}
4955
36bb2ca2 4956- (void) _finishLoading {
1d3b229d
JF
4957 if (!reloading_) {
4958 loading_ = false;
4959 [indicator_ stopAnimation];
4960 [self reloadButtons];
4961 }
36bb2ca2 4962}
b4d89997 4963
7d2ac47f 4964- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
7d2ac47f
JF
4965 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
4966}
4967
4968- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
4969 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
4970}
4971
4972- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
4973 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
4974}
4975
4976- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
4977 return [webview_ webView:sender didCommitLoadForFrame:frame];
4978}
4979
4980- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
4981 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
4982}
4983
36bb2ca2 4984- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
7d2ac47f
JF
4985 if ([frame parentFrame] == nil)
4986 [self _finishLoading];
4987 return [webview_ webView:sender didFinishLoadForFrame:frame];
36bb2ca2 4988}
b4d89997 4989
36bb2ca2
JF
4990- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4991 if ([frame parentFrame] != nil)
4992 return;
36bb2ca2 4993 [self _finishLoading];
7b0ce2da 4994
6d9712c4 4995 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
7b0ce2da 4996 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
6d9712c4
JF
4997 [[error localizedDescription] stringByAddingPercentEscapes]
4998 ]]];
36bb2ca2
JF
4999}
5000
c390d3ab
JF
5001- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5002#if ForSaurik
5003 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5004#endif
5005}
5006
770f2a8e 5007- (id) initWithBook:(RVBook *)book {
36bb2ca2 5008 if ((self = [super initWithBook:book]) != nil) {
36bb2ca2
JF
5009 loading_ = false;
5010
5011 struct CGRect bounds = [self bounds];
5012
5013 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5014 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5015 [self addSubview:pinstripe];
5016
5017 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5018 [self addSubview:scroller_];
5019
5020 [scroller_ setScrollingEnabled:YES];
5021 [scroller_ setAdjustForContentSizeChange:YES];
5022 [scroller_ setClipsSubviews:YES];
5023 [scroller_ setAllowsRubberBanding:YES];
5024 [scroller_ setScrollDecelerationFactor:0.99];
5025 [scroller_ setDelegate:self];
5026
5027 CGRect webrect = [scroller_ bounds];
5028 webrect.size.height = 0;
5029
affeffc7
JF
5030 WebView *webview;
5031
5032#if RecycleWebViews
baf80942
JF
5033 webview_ = [Documents_ lastObject];
5034 if (webview_ != nil) {
5035 webview_ = [webview_ retain];
affeffc7 5036 webview = [webview_ webView];
baf80942
JF
5037 [Documents_ removeLastObject];
5038 [webview_ setFrame:webrect];
5039 } else {
affeffc7
JF
5040#else
5041 if (true) {
5042#endif
baf80942 5043 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
affeffc7 5044 webview = [webview_ webView];
baf80942
JF
5045
5046 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
36bb2ca2 5047
affeffc7
JF
5048 [webview_ setAllowsMessaging:YES];
5049
baf80942 5050 [webview_ setTilingEnabled:YES];
affeffc7
JF
5051 [webview_ setDrawsGrid:NO];
5052 [webview_ setLogsTilingChanges:NO];
baf80942 5053 [webview_ setTileMinificationFilter:kCAFilterNearest];
affeffc7 5054 [webview_ setDetectsPhoneNumbers:NO];
baf80942 5055 [webview_ setAutoresizes:YES];
7d2ac47f 5056
baf80942
JF
5057 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5058 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5059 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
7d2ac47f 5060
baf80942 5061 [webview_ _setDocumentType:0x4];
7d2ac47f 5062
baf80942
JF
5063 [webview_ setZoomsFocusedFormControl:YES];
5064 [webview_ setContentsPosition:7];
5065 [webview_ setEnabledGestures:0xa];
5066 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
5067 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
affeffc7 5068
baf80942 5069 [webview_ setSmoothsFonts:YES];
c390d3ab 5070
affeffc7
JF
5071 [webview _setUsesLoaderCache:YES];
5072 [webview setGroupName:@"Cydia"];
5073 //[webview _setLayoutInterval:0.5];
baf80942 5074 }
7d2ac47f 5075
36bb2ca2 5076 [webview_ setDelegate:self];
7d2ac47f 5077 [webview_ setGestureDelegate:self];
baf80942 5078 [scroller_ addSubview:webview_];
b4d89997 5079
c390d3ab
JF
5080 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5081
affeffc7 5082 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
baf80942 5083 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
affeffc7 5084 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
36bb2ca2 5085
770f2a8e 5086 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
36bb2ca2
JF
5087 NSString *application = package == nil ? @"Cydia" : [NSString
5088 stringWithFormat:@"Cydia/%@",
5089 [package installed]
affeffc7 5090 ]; [webview setApplicationNameForUserAgent:application];
36bb2ca2 5091
baf80942
JF
5092 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5093
36bb2ca2 5094 [webview setFrameLoadDelegate:self];
baf80942 5095 [webview setResourceLoadDelegate:indirect_];
36bb2ca2 5096 [webview setUIDelegate:self];
c390d3ab
JF
5097 [webview setScriptDebugDelegate:self];
5098 [webview setPolicyDelegate:self];
36bb2ca2
JF
5099
5100 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
baf80942
JF
5101
5102 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5103 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5104 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
36bb2ca2 5105 } return self;
dc5812ec
JF
5106}
5107
7d2ac47f
JF
5108- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5109 [webview_ redrawScaledDocument];
5110}
5111
36bb2ca2 5112- (void) _rightButtonClicked {
1d3b229d 5113 reloading_ = true;
36bb2ca2
JF
5114 [self reloadURL];
5115}
5116
6f1a15d9
JF
5117- (NSString *) _rightButtonTitle {
5118 return @"Reload";
5119}
5120
36bb2ca2 5121- (NSString *) rightButtonTitle {
6f1a15d9 5122 return loading_ ? @"" : [self _rightButtonTitle];
36bb2ca2
JF
5123}
5124
5125- (NSString *) title {
affeffc7 5126 return title_ == nil ? @"Loading" : title_;
36bb2ca2
JF
5127}
5128
5129- (NSString *) backButtonTitle {
5130 return @"Browser";
dc5812ec
JF
5131}
5132
36bb2ca2 5133- (void) setPageActive:(BOOL)active {
baf80942 5134 if (!active)
36bb2ca2 5135 [indicator_ removeFromSuperview];
baf80942
JF
5136 else
5137 [[book_ navigationBar] addSubview:indicator_];
dc5812ec
JF
5138}
5139
36bb2ca2 5140- (void) resetViewAnimated:(BOOL)animated {
b4d89997 5141}
2367a917 5142
a0376fc1
JF
5143- (void) setPushed:(bool)pushed {
5144 pushed_ = pushed;
5145}
5146
7b0ce2da
JF
5147@end
5148/* }}} */
aa5e5990 5149
807ae6d7 5150/* Cydia Book {{{ */
7b0ce2da
JF
5151@interface CYBook : RVBook <
5152 ProgressDelegate
5153> {
5154 _transient Database *database_;
baf80942 5155 UINavigationBar *overlay_;
affeffc7 5156 UINavigationBar *underlay_;
7b0ce2da
JF
5157 UIProgressIndicator *indicator_;
5158 UITextLabel *prompt_;
5159 UIProgressBar *progress_;
baf80942 5160 UINavigationButton *cancel_;
7b0ce2da 5161 bool updating_;
baf80942
JF
5162 size_t received_;
5163 NSTimeInterval last_;
aa5e5990
JF
5164}
5165
7b0ce2da
JF
5166- (id) initWithFrame:(CGRect)frame database:(Database *)database;
5167- (void) update;
5168- (BOOL) updating;
aa5e5990
JF
5169
5170@end
aa5e5990 5171
807ae6d7 5172@implementation CYBook
dc5812ec 5173
2367a917 5174- (void) dealloc {
807ae6d7
JF
5175 [overlay_ release];
5176 [indicator_ release];
5177 [prompt_ release];
5178 [progress_ release];
5179 [cancel_ release];
2367a917
JF
5180 [super dealloc];
5181}
5182
807ae6d7
JF
5183- (NSString *) getTitleForPage:(RVPage *)page {
5184 return Simplify([super getTitleForPage:page]);
b4d89997 5185}
dc5812ec 5186
807ae6d7
JF
5187- (BOOL) updating {
5188 return updating_;
6d9712c4
JF
5189}
5190
807ae6d7
JF
5191- (void) update {
5192 [UIView beginAnimations:nil context:NULL];
dc5812ec 5193
807ae6d7
JF
5194 CGRect ovrframe = [overlay_ frame];
5195 ovrframe.origin.y = 0;
5196 [overlay_ setFrame:ovrframe];
dc5812ec 5197
807ae6d7
JF
5198 CGRect barframe = [navbar_ frame];
5199 barframe.origin.y += ovrframe.size.height;
5200 [navbar_ setFrame:barframe];
dc5812ec 5201
807ae6d7
JF
5202 CGRect trnframe = [transition_ frame];
5203 trnframe.origin.y += ovrframe.size.height;
5204 trnframe.size.height -= ovrframe.size.height;
5205 [transition_ setFrame:trnframe];
5a09ae08 5206
807ae6d7 5207 [UIView endAnimations];
2367a917 5208
807ae6d7
JF
5209 [indicator_ startAnimation];
5210 [prompt_ setText:@"Updating Database"];
5211 [progress_ setProgress:0];
2367a917 5212
807ae6d7
JF
5213 received_ = 0;
5214 last_ = [NSDate timeIntervalSinceReferenceDate];
5215 updating_ = true;
5216 [overlay_ addSubview:cancel_];
2367a917 5217
807ae6d7
JF
5218 [NSThread
5219 detachNewThreadSelector:@selector(_update)
5220 toTarget:self
5221 withObject:nil
5222 ];
b4d89997 5223}
2367a917 5224
807ae6d7
JF
5225- (void) _update_ {
5226 updating_ = false;
36bb2ca2 5227
807ae6d7 5228 [indicator_ stopAnimation];
2367a917 5229
807ae6d7 5230 [UIView beginAnimations:nil context:NULL];
6d9712c4 5231
807ae6d7
JF
5232 CGRect ovrframe = [overlay_ frame];
5233 ovrframe.origin.y = -ovrframe.size.height;
5234 [overlay_ setFrame:ovrframe];
dc5812ec 5235
807ae6d7
JF
5236 CGRect barframe = [navbar_ frame];
5237 barframe.origin.y -= ovrframe.size.height;
5238 [navbar_ setFrame:barframe];
dc5812ec 5239
807ae6d7
JF
5240 CGRect trnframe = [transition_ frame];
5241 trnframe.origin.y -= ovrframe.size.height;
5242 trnframe.size.height += ovrframe.size.height;
5243 [transition_ setFrame:trnframe];
2367a917 5244
807ae6d7 5245 [UIView commitAnimations];
baf80942 5246
807ae6d7 5247 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
2367a917
JF
5248}
5249
807ae6d7
JF
5250- (id) initWithFrame:(CGRect)frame database:(Database *)database {
5251 if ((self = [super initWithFrame:frame]) != nil) {
5252 database_ = database;
36bb2ca2 5253
807ae6d7
JF
5254 CGRect ovrrect = [navbar_ bounds];
5255 ovrrect.size.height = [UINavigationBar defaultSize].height;
5256 ovrrect.origin.y = -ovrrect.size.height;
6d9712c4 5257
807ae6d7
JF
5258 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5259 [self addSubview:overlay_];
dc5812ec 5260
807ae6d7
JF
5261 ovrrect.origin.y = frame.size.height;
5262 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5263 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5264 [self addSubview:underlay_];
6d9712c4 5265
807ae6d7
JF
5266 [overlay_ setBarStyle:1];
5267 [underlay_ setBarStyle:1];
5268
5269 int barstyle = [overlay_ _barStyle:NO];
5270 bool ugly = barstyle == 0;
5271
5272 UIProgressIndicatorStyle style = ugly ?
5273 UIProgressIndicatorStyleMediumBrown :
5274 UIProgressIndicatorStyleMediumWhite;
5275
5276 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5277 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5278 CGRect indrect = {{indoffset, indoffset}, indsize};
5279
5280 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5281 [indicator_ setStyle:style];
5282 [overlay_ addSubview:indicator_];
5283
5284 CGSize prmsize = {215, indsize.height + 4};
5285
5286 CGRect prmrect = {{
5287 indoffset * 2 + indsize.width,
5288#ifdef __OBJC2__
5289 -1 +
5290#endif
5291 unsigned(ovrrect.size.height - prmsize.height) / 2
5292 }, prmsize};
5293
5294 UIFont *font = [UIFont systemFontOfSize:15];
5295
5296 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5297
5298 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5299 [prompt_ setBackgroundColor:[UIColor clearColor]];
5300 [prompt_ setFont:font];
5301
5302 [overlay_ addSubview:prompt_];
5303
5304 CGSize prgsize = {75, 100};
5305
5306 CGRect prgrect = {{
5307 ovrrect.size.width - prgsize.width - 10,
5308 (ovrrect.size.height - prgsize.height) / 2
5309 } , prgsize};
5310
5311 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5312 [progress_ setStyle:0];
5313 [overlay_ addSubview:progress_];
5314
5315 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5316 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5317
5318 CGRect frame = [cancel_ frame];
5319 frame.size.width = 65;
5320 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5321 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5322 [cancel_ setFrame:frame];
5323
5324 [cancel_ setBarStyle:barstyle];
5325 } return self;
5326}
5327
5328- (void) _onCancel {
5329 updating_ = false;
5330 [cancel_ removeFromSuperview];
5331}
5332
5333- (void) _update { _pooled
5334 Status status;
5335 status.setDelegate(self);
5336
5337 [database_ updateWithStatus:status];
5338
5339 [self
5340 performSelectorOnMainThread:@selector(_update_)
5341 withObject:nil
5342 waitUntilDone:NO
5343 ];
5344}
5345
5346- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5347 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5348}
5349
5350- (void) setProgressTitle:(NSString *)title {
5351 [self
5352 performSelectorOnMainThread:@selector(_setProgressTitle:)
5353 withObject:title
5354 waitUntilDone:YES
5355 ];
5356}
5357
5358- (void) setProgressPercent:(float)percent {
5359 [self
5360 performSelectorOnMainThread:@selector(_setProgressPercent:)
5361 withObject:[NSNumber numberWithFloat:percent]
5362 waitUntilDone:YES
5363 ];
5364}
5365
5366- (void) startProgress {
5367}
5368
5369- (void) addProgressOutput:(NSString *)output {
5370 [self
5371 performSelectorOnMainThread:@selector(_addProgressOutput:)
5372 withObject:output
5373 waitUntilDone:YES
5374 ];
5375}
5376
5377- (bool) isCancelling:(size_t)received {
5378 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5379 if (received_ != received) {
5380 received_ = received;
5381 last_ = now;
5382 } else if (now - last_ > 15)
5383 return true;
5384 return !updating_;
5385}
5386
5387- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5388 [sheet dismiss];
5389}
5390
5391- (void) _setProgressTitle:(NSString *)title {
5392 [prompt_ setText:title];
5393}
5394
5395- (void) _setProgressPercent:(NSNumber *)percent {
5396 [progress_ setProgress:[percent floatValue]];
5397}
5398
5399- (void) _addProgressOutput:(NSString *)output {
5400}
5401
5402@end
5403/* }}} */
5404/* Cydia:// Protocol {{{ */
5405@interface CydiaURLProtocol : NSURLProtocol {
5406}
5407
5408@end
5409
5410@implementation CydiaURLProtocol
5411
5412+ (BOOL) canInitWithRequest:(NSURLRequest *)request {
5413 NSURL *url([request URL]);
5414 if (url == nil)
5415 return NO;
5416 NSString *scheme([[url scheme] lowercaseString]);
5417 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5418 return NO;
5419 return YES;
5420}
5421
5422+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5423 return request;
5424}
5425
5426- (void) startLoading {
5427 id<NSURLProtocolClient> client([self client]);
5428 NSURLRequest *request([self request]);
5429
5430 NSURL *url([request URL]);
5431 NSString *href([url absoluteString]);
5432
5433 NSString *path([href substringFromIndex:8]);
5434 NSRange slash([path rangeOfString:@"/"]);
5435
5436 NSString *command;
5437 if (slash.location == NSNotFound) {
5438 command = path;
5439 path = nil;
5440 } else {
5441 command = [path substringToIndex:slash.location];
5442 path = [path substringFromIndex:(slash.location + 1)];
5443 }
5444
5445 Database *database([Database sharedInstance]);
5446
5447 if ([command isEqualToString:@"package-icon"]) {
5448 if (path == nil)
5449 goto fail;
5450 Package *package([database packageWithName:path]);
5451 if (package == nil)
5452 goto fail;
5453
5454 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5455
5456 UIImage *icon([package icon]);
5457 NSData *data(UIImagePNGRepresentation(icon));
5458
5459 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5460 [client URLProtocol:self didLoadData:data];
5461 [client URLProtocolDidFinishLoading:self];
5462 } else fail: {
5463 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5464 }
5465}
5466
5467- (void) stopLoading {
5468}
5469
5470@end
5471/* }}} */
5472
5473/* Install View {{{ */
5474@interface InstallView : RVPage {
5475 _transient Database *database_;
5476 NSMutableArray *sections_;
5477 NSMutableArray *filtered_;
5478 UITransitionView *transition_;
5479 UITable *list_;
5480 UIView *accessory_;
5481 BOOL editing_;
5482}
5483
5484- (id) initWithBook:(RVBook *)book database:(Database *)database;
5485- (void) reloadData;
5486- (void) resetView;
5487
5488@end
5489
5490@implementation InstallView
5491
5492- (void) dealloc {
5493 [list_ setDataSource:nil];
5494 [list_ setDelegate:nil];
5495
5496 [sections_ release];
5497 [filtered_ release];
5498 [transition_ release];
5499 [list_ release];
5500 [accessory_ release];
5501 [super dealloc];
5502}
5503
5504- (int) numberOfRowsInTable:(UITable *)table {
5505 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5506}
5507
5508- (float) table:(UITable *)table heightForRow:(int)row {
5509 return 45;
5510}
5511
5512- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5513 if (reusing == nil)
5514 reusing = [[[SectionCell alloc] init] autorelease];
5515 [(SectionCell *)reusing setSection:(editing_ ?
5516 [sections_ objectAtIndex:row] :
5517 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5518 ) editing:editing_];
5519 return reusing;
5520}
5521
5522- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5523 return !editing_;
5524}
5525
5526- (BOOL) table:(UITable *)table canSelectRow:(int)row {
5527 return !editing_;
5528}
5529
5530- (void) tableRowSelected:(NSNotification *)notification {
5531 int row = [[notification object] selectedRow];
5532 if (row == INT_MAX)
5533 return;
5534
5535 Section *section;
5536 NSString *name;
5537 NSString *title;
5538
5539 if (row == 0) {
5540 section = nil;
5541 name = nil;
5542 title = @"All Packages";
5543 } else {
5544 section = [filtered_ objectAtIndex:(row - 1)];
5545 name = [section name];
5546
5547 if (name != nil)
5548 title = name;
5549 else {
5550 name = @"";
5551 title = @"(No Section)";
5552 }
5553 }
5554
5555 PackageTable *table = [[[PackageTable alloc]
5556 initWithBook:book_
5557 database:database_
5558 title:title
5559 filter:@selector(isVisiblyUninstalledInSection:)
5560 with:name
5561 ] autorelease];
5562
5563 [table setDelegate:delegate_];
5564
5565 [book_ pushPage:table];
5566}
5567
5568- (id) initWithBook:(RVBook *)book database:(Database *)database {
5569 if ((self = [super initWithBook:book]) != nil) {
5570 database_ = database;
5571
5572 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5573 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5574
5575 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5576 [self addSubview:transition_];
5577
5578 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5579 [transition_ transition:0 toView:list_];
5580
5581 UITableColumn *column = [[[UITableColumn alloc]
5582 initWithTitle:@"Name"
5583 identifier:@"name"
5584 width:[self frame].size.width
5585 ] autorelease];
5586
5587 [list_ setDataSource:self];
5588 [list_ setSeparatorStyle:1];
5589 [list_ addTableColumn:column];
5590 [list_ setDelegate:self];
5591 [list_ setReusesTableCells:YES];
5592
5593 [self reloadData];
5594
5595 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5596 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5597 } return self;
5598}
5599
5600- (void) reloadData {
5601 NSArray *packages = [database_ packages];
5602
5603 [sections_ removeAllObjects];
5604 [filtered_ removeAllObjects];
5605
5606 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5607 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5608
5609 _trace();
5610 for (size_t i(0); i != [packages count]; ++i) {
5611 Package *package([packages objectAtIndex:i]);
5612 NSString *name([package section]);
5613
5614 if (name != nil) {
5615 Section *section([sections objectForKey:name]);
5616 if (section == nil) {
5617 section = [[[Section alloc] initWithName:name] autorelease];
5618 [sections setObject:section forKey:name];
5619 }
5620 }
6d9712c4
JF
5621
5622 if ([package valid] && [package installed] == nil && [package visible])
5623 [filtered addObject:package];
b4d89997 5624 }
807ae6d7 5625 _trace();
dc5812ec 5626
6d9712c4
JF
5627 [sections_ addObjectsFromArray:[sections allValues]];
5628 [sections_ sortUsingSelector:@selector(compareByName:)];
5629
807ae6d7 5630 _trace();
6d9712c4 5631 [filtered sortUsingSelector:@selector(compareBySection:)];
807ae6d7 5632 _trace();
dc5812ec 5633
b4d89997 5634 Section *section = nil;
6d9712c4
JF
5635 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5636 Package *package = [filtered objectAtIndex:offset];
b4d89997 5637 NSString *name = [package section];
2367a917 5638
36bb2ca2 5639 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
6d9712c4
JF
5640 section = name == nil ?
5641 [[[Section alloc] initWithName:nil] autorelease] :
5642 [sections objectForKey:name];
5643 [filtered_ addObject:section];
b4d89997 5644 }
dc5812ec 5645
36bb2ca2 5646 [section addToCount];
b4d89997 5647 }
807ae6d7 5648 _trace();
2367a917 5649
b4d89997 5650 [list_ reloadData];
807ae6d7 5651 _trace();
36bb2ca2 5652}
2367a917 5653
6d9712c4
JF
5654- (void) resetView {
5655 if (editing_)
5656 [self _rightButtonClicked];
5657}
5658
36bb2ca2
JF
5659- (void) resetViewAnimated:(BOOL)animated {
5660 [list_ resetViewAnimated:animated];
5661}
2367a917 5662
6d9712c4
JF
5663- (void) _rightButtonClicked {
5664 if ((editing_ = !editing_))
5665 [list_ reloadData];
807ae6d7 5666 else
6d9712c4 5667 [delegate_ updateData];
affeffc7 5668 [book_ reloadTitleForPage:self];
6d9712c4
JF
5669 [book_ reloadButtonsForPage:self];
5670}
5671
36bb2ca2 5672- (NSString *) title {
6d9712c4 5673 return editing_ ? @"Section Visibility" : @"Install by Section";
2367a917
JF
5674}
5675
36bb2ca2
JF
5676- (NSString *) backButtonTitle {
5677 return @"Sections";
dc5812ec
JF
5678}
5679
6d9712c4
JF
5680- (NSString *) rightButtonTitle {
5681 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5682}
5683
baf80942
JF
5684- (UINavigationButtonStyle) rightButtonStyle {
5685 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5686}
5687
a54b1c10
JF
5688- (UIView *) accessoryView {
5689 return accessory_;
5690}
5691
dc5812ec 5692@end
2367a917 5693/* }}} */
b4d89997 5694/* Changes View {{{ */
36bb2ca2
JF
5695@interface ChangesView : RVPage {
5696 _transient Database *database_;
dc5812ec
JF
5697 NSMutableArray *packages_;
5698 NSMutableArray *sections_;
36bb2ca2
JF
5699 UISectionList *list_;
5700 unsigned upgrades_;
dc5812ec
JF
5701}
5702
36bb2ca2
JF
5703- (id) initWithBook:(RVBook *)book database:(Database *)database;
5704- (void) reloadData;
dc5812ec 5705
dc5812ec
JF
5706@end
5707
b4d89997
JF
5708@implementation ChangesView
5709
5710- (void) dealloc {
36bb2ca2
JF
5711 [[list_ table] setDelegate:nil];
5712 [list_ setDataSource:nil];
5713
b4d89997
JF
5714 [packages_ release];
5715 [sections_ release];
36bb2ca2 5716 [list_ release];
b4d89997
JF
5717 [super dealloc];
5718}
dc5812ec
JF
5719
5720- (int) numberOfSectionsInSectionList:(UISectionList *)list {
5721 return [sections_ count];
5722}
5723
5724- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5725 return [[sections_ objectAtIndex:section] name];
5726}
5727
5728- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5729 return [[sections_ objectAtIndex:section] row];
5730}
5731
5732- (int) numberOfRowsInTable:(UITable *)table {
5733 return [packages_ count];
5734}
5735
5736- (float) table:(UITable *)table heightForRow:(int)row {
9bedffaa 5737 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
dc5812ec
JF
5738}
5739
1d80f6b9
JF
5740- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5741 if (reusing == nil)
36bb2ca2 5742 reusing = [[[PackageCell alloc] init] autorelease];
1d80f6b9
JF
5743 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5744 return reusing;
dc5812ec
JF
5745}
5746
5747- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
b4d89997 5748 return NO;
dc5812ec
JF
5749}
5750
b4d89997
JF
5751- (void) tableRowSelected:(NSNotification *)notification {
5752 int row = [[notification object] selectedRow];
36bb2ca2
JF
5753 if (row == INT_MAX)
5754 return;
5755 Package *package = [packages_ objectAtIndex:row];
5756 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5757 [view setDelegate:delegate_];
5758 [view setPackage:package];
5759 [book_ pushPage:view];
dc5812ec
JF
5760}
5761
9a7b04c5
JF
5762- (void) _leftButtonClicked {
5763 [(CYBook *)book_ update];
5764 [self reloadButtons];
5765}
5766
36bb2ca2
JF
5767- (void) _rightButtonClicked {
5768 [delegate_ distUpgrade];
dc5812ec
JF
5769}
5770
36bb2ca2
JF
5771- (id) initWithBook:(RVBook *)book database:(Database *)database {
5772 if ((self = [super initWithBook:book]) != nil) {
5773 database_ = database;
dc5812ec 5774
b4d89997
JF
5775 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5776 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 5777
36bb2ca2
JF
5778 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5779 [self addSubview:list_];
5780
3178d79b 5781 [list_ setShouldHideHeaderInShortLists:NO];
b4d89997
JF
5782 [list_ setDataSource:self];
5783 //[list_ setSectionListStyle:1];
dc5812ec 5784
b4d89997 5785 UITableColumn *column = [[[UITableColumn alloc]
dc5812ec
JF
5786 initWithTitle:@"Name"
5787 identifier:@"name"
36bb2ca2 5788 width:[self frame].size.width
b4d89997 5789 ] autorelease];
dc5812ec
JF
5790
5791 UITable *table = [list_ table];
5792 [table setSeparatorStyle:1];
5793 [table addTableColumn:column];
5794 [table setDelegate:self];
1d80f6b9 5795 [table setReusesTableCells:YES];
dc5812ec 5796
36bb2ca2 5797 [self reloadData];
baf80942
JF
5798
5799 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5800 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
dc5812ec
JF
5801 } return self;
5802}
5803
36bb2ca2
JF
5804- (void) reloadData {
5805 NSArray *packages = [database_ packages];
5806
b4d89997 5807 [packages_ removeAllObjects];
36bb2ca2
JF
5808 [sections_ removeAllObjects];
5809
807ae6d7 5810 _trace();
b4d89997
JF
5811 for (size_t i(0); i != [packages count]; ++i) {
5812 Package *package([packages objectAtIndex:i]);
6d9712c4
JF
5813
5814 if (
5815 [package installed] == nil && [package valid] && [package visible] ||
5816 [package upgradableAndEssential:NO]
5817 )
b4d89997
JF
5818 [packages_ addObject:package];
5819 }
2367a917 5820
807ae6d7
JF
5821 _trace();
5822 [packages_ radixUsingSelector:@selector(compareForChanges) withObject:nil];
5823 _trace();
dc5812ec 5824
6d9712c4 5825 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
807ae6d7 5826 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
dc5812ec 5827 Section *section = nil;
807ae6d7 5828 NSDate *last = nil;
b4d89997 5829
36bb2ca2
JF
5830 upgrades_ = 0;
5831 bool unseens = false;
5832
199d0ba5 5833 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
36bb2ca2 5834
807ae6d7 5835 _trace();
dc5812ec
JF
5836 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
5837 Package *package = [packages_ objectAtIndex:offset];
dc5812ec 5838
807ae6d7 5839 if (![package upgradableAndEssential:YES]) {
36bb2ca2 5840 unseens = true;
b4d89997 5841 NSDate *seen = [package seen];
dc5812ec 5842
807ae6d7
JF
5843 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
5844 last = seen;
723a0072 5845
807ae6d7 5846 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
b4d89997
JF
5847 section = [[[Section alloc] initWithName:name row:offset] autorelease];
5848 [sections_ addObject:section];
807ae6d7 5849 [name release];
b4d89997
JF
5850 }
5851
36bb2ca2 5852 [section addToCount];
807ae6d7
JF
5853 } else if ([package ignored])
5854 [ignored addToCount];
5855 else {
5856 ++upgrades_;
5857 [upgradable addToCount];
b4d89997 5858 }
dc5812ec 5859 }
807ae6d7 5860 _trace();
dc5812ec 5861
36bb2ca2 5862 CFRelease(formatter);
b4d89997 5863
36bb2ca2
JF
5864 if (unseens) {
5865 Section *last = [sections_ lastObject];
5866 size_t count = [last count];
5867 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5868 [sections_ removeLastObject];
5869 }
dc5812ec 5870
807ae6d7
JF
5871 if ([ignored count] != 0)
5872 [sections_ insertObject:ignored atIndex:0];
36bb2ca2
JF
5873 if (upgrades_ != 0)
5874 [sections_ insertObject:upgradable atIndex:0];
c25a610d 5875
36bb2ca2
JF
5876 [list_ reloadData];
5877 [self reloadButtons];
3178d79b
JF
5878}
5879
36bb2ca2
JF
5880- (void) resetViewAnimated:(BOOL)animated {
5881 [list_ resetViewAnimated:animated];
3178d79b
JF
5882}
5883
9a7b04c5
JF
5884- (NSString *) leftButtonTitle {
5885 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5886}
5887
36bb2ca2 5888- (NSString *) rightButtonTitle {
f641a0e5 5889 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
3178d79b
JF
5890}
5891
36bb2ca2
JF
5892- (NSString *) title {
5893 return @"Changes";
8fe19fc1
JF
5894}
5895
3178d79b
JF
5896@end
5897/* }}} */
36bb2ca2
JF
5898/* Search View {{{ */
5899@protocol SearchViewDelegate
5900- (void) showKeyboard:(BOOL)show;
5901@end
b4d89997 5902
a0be02eb 5903@interface SearchView : RVPage {
36bb2ca2
JF
5904 UIView *accessory_;
5905 UISearchField *field_;
a0be02eb
JF
5906 UITransitionView *transition_;
5907 PackageTable *table_;
5908 UIPreferencesTable *advanced_;
9b619239 5909 UIView *dimmed_;
a0be02eb 5910 bool flipped_;
ab398adf 5911 bool reload_;
36bb2ca2 5912}
b4d89997 5913
36bb2ca2
JF
5914- (id) initWithBook:(RVBook *)book database:(Database *)database;
5915- (void) reloadData;
b4d89997 5916
3178d79b
JF
5917@end
5918
36bb2ca2 5919@implementation SearchView
3178d79b 5920
b4d89997 5921- (void) dealloc {
36bb2ca2 5922 [field_ setDelegate:nil];
3178d79b 5923
36bb2ca2
JF
5924 [accessory_ release];
5925 [field_ release];
a0be02eb
JF
5926 [transition_ release];
5927 [table_ release];
5928 [advanced_ release];
9b619239 5929 [dimmed_ release];
36bb2ca2 5930 [super dealloc];
b4d89997
JF
5931}
5932
ab398adf
JF
5933- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5934 return 1;
5935}
5936
5937- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5938 switch (group) {
5939 case 0: return @"Advanced Search (Coming Soon!)";
5940
5941 default: _assert(false);
5942 }
5943}
5944
5945- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5946 switch (group) {
5947 case 0: return 0;
5948
5949 default: _assert(false);
5950 }
5951}
5952
2388b078
JF
5953- (void) _showKeyboard:(BOOL)show {
5954 CGSize keysize = [UIKeyboard defaultSize];
5955 CGRect keydown = [book_ pageBounds];
5956 CGRect keyup = keydown;
5957 keyup.size.height -= keysize.height - ButtonBarHeight_;
5958
5959 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5960
5961 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5962 [animation setSignificantRectFields:8];
5963
5964 if (show) {
5965 [animation setStartFrame:keydown];
5966 [animation setEndFrame:keyup];
5967 } else {
5968 [animation setStartFrame:keyup];
5969 [animation setEndFrame:keydown];
5970 }
5971
5972 UIAnimator *animator = [UIAnimator sharedAnimator];
5973
5974 [animator
5975 addAnimations:[NSArray arrayWithObjects:animation, nil]
5976 withDuration:(KeyboardTime_ - delay)
5977 start:!show
5978 ];
5979
5980 if (show)
5981 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5982
5983 [delegate_ showKeyboard:show];
5984}
5985
36bb2ca2 5986- (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2388b078 5987 [self _showKeyboard:YES];
36bb2ca2 5988}
b4d89997 5989
36bb2ca2 5990- (void) textFieldDidResignFirstResponder:(UITextField *)field {
2388b078 5991 [self _showKeyboard:NO];
36bb2ca2 5992}
b4d89997 5993
36bb2ca2 5994- (void) keyboardInputChanged:(UIFieldEditor *)editor {
ab398adf
JF
5995 if (reload_) {
5996 NSString *text([field_ text]);
5997 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5998 [self reloadData];
5999 reload_ = false;
6000 }
6001}
6002
6003- (void) textFieldClearButtonPressed:(UITextField *)field {
6004 reload_ = true;
6005}
6006
6007- (void) keyboardInputShouldDelete:(id)input {
6008 reload_ = true;
36bb2ca2 6009}
b4d89997 6010
36bb2ca2 6011- (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
ab398adf
JF
6012 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6013 reload_ = true;
36bb2ca2 6014 return YES;
ab398adf
JF
6015 } else {
6016 [field_ resignFirstResponder];
6017 return NO;
6018 }
b4d89997
JF
6019}
6020
36bb2ca2 6021- (id) initWithBook:(RVBook *)book database:(Database *)database {
a0be02eb
JF
6022 if ((self = [super initWithBook:book]) != nil) {
6023 CGRect pageBounds = [book_ pageBounds];
6024
7fea16f2 6025 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
a0be02eb 6026 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
7fea16f2 6027 [self addSubview:pinstripe];*/
a0be02eb 6028
a0be02eb
JF
6029 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6030 [self addSubview:transition_];
6031
6032 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6033
ab398adf
JF
6034 [advanced_ setReusesTableCells:YES];
6035 [advanced_ setDataSource:self];
6036 [advanced_ reloadData];
6037
9b619239
JF
6038 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6039 CGColor dimmed(space_, 0, 0, 0, 0.5);
a9543575 6040 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
9b619239 6041
a0be02eb
JF
6042 table_ = [[PackageTable alloc]
6043 initWithBook:book
6044 database:database
6045 title:nil
7d2ac47f 6046 filter:@selector(isUnfilteredAndSearchedForBy:)
a0be02eb
JF
6047 with:nil
6048 ];
6049
ab398adf 6050 [table_ setShouldHideHeaderInShortLists:NO];
a0be02eb
JF
6051 [transition_ transition:0 toView:table_];
6052
8cc774c5
JF
6053 CGRect cnfrect = {{
6054#ifdef __OBJC2__
6055 6 +
6056#endif
6057 1, 38}, {17, 18}};
b4d89997 6058
36bb2ca2 6059 CGRect area;
6d9712c4 6060 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
baf80942 6061 area.origin.y = 1;
8cc774c5
JF
6062
6063 area.size.width =
6064#ifdef __OBJC2__
6065 8 +
6066#endif
6067 [self bounds].size.width - area.origin.x - 18;
6068
36bb2ca2 6069 area.size.height = [UISearchField defaultHeight];
3178d79b 6070
36bb2ca2 6071 field_ = [[UISearchField alloc] initWithFrame:area];
b4d89997 6072
a9543575 6073 UIFont *font = [UIFont systemFontOfSize:16];
36bb2ca2 6074 [field_ setFont:font];
b4d89997 6075
36bb2ca2 6076 [field_ setPlaceholder:@"Package Names & Descriptions"];
36bb2ca2 6077 [field_ setDelegate:self];
b4d89997 6078
87f46a96 6079 [field_ setPaddingTop:5];
8cc774c5 6080
a9543575
JF
6081 UITextInputTraits *traits = [field_ textInputTraits];
6082 [traits setAutocapitalizationType:0];
6083 [traits setAutocorrectionType:1];
6d9712c4
JF
6084 [traits setReturnKeyType:6];
6085
baf80942 6086 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
8cc774c5
JF
6087
6088 accessory_ = [[UIView alloc] initWithFrame:accrect];
795d26fc
JF
6089 [accessory_ addSubview:field_];
6090
6d9712c4 6091 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
36bb2ca2 6092 [configure setShowPressFeedback:YES];
559dec34 6093 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
36bb2ca2 6094 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6d9712c4 6095 [accessory_ addSubview:configure];*/
baf80942
JF
6096
6097 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6098 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
36bb2ca2 6099 } return self;
3178d79b
JF
6100}
6101
a0be02eb 6102- (void) flipPage {
199d0ba5 6103#ifndef __OBJC2__
a0be02eb
JF
6104 LKAnimation *animation = [LKTransition animation];
6105 [animation setType:@"oglFlip"];
6106 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6107 [animation setFillMode:@"extended"];
6108 [animation setTransitionFlags:3];
6109 [animation setDuration:10];
6110 [animation setSpeed:0.35];
7fea16f2
JF
6111 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6112 [[transition_ _layer] addAnimation:animation forKey:0];
a0be02eb
JF
6113 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6114 flipped_ = !flipped_;
199d0ba5 6115#endif
a0be02eb
JF
6116}
6117
36bb2ca2 6118- (void) configurePushed {
a0be02eb
JF
6119 [field_ resignFirstResponder];
6120 [self flipPage];
6121}
6122
807ae6d7
JF
6123- (void) resetViewAnimated:(BOOL)animated {
6124 if (flipped_)
6125 [self flipPage];
6126 [table_ resetViewAnimated:animated];
6127}
3178d79b 6128
807ae6d7
JF
6129- (void) _reloadData {
6130}
b4d89997 6131
807ae6d7
JF
6132- (void) reloadData {
6133 if (flipped_)
6134 [self flipPage];
6135 [table_ setObject:[field_ text]];
6136 [table_ reloadData];
6137 [table_ resetCursor];
6138}
affeffc7 6139
807ae6d7
JF
6140- (UIView *) accessoryView {
6141 return accessory_;
6142}
affeffc7 6143
807ae6d7
JF
6144- (NSString *) title {
6145 return nil;
6146}
dddbc481 6147
807ae6d7
JF
6148- (NSString *) backButtonTitle {
6149 return @"Search";
6150}
795d26fc 6151
807ae6d7
JF
6152- (void) setDelegate:(id)delegate {
6153 [table_ setDelegate:delegate];
6154 [super setDelegate:delegate];
6155}
8fe19fc1 6156
807ae6d7
JF
6157@end
6158/* }}} */
b4d89997 6159
807ae6d7
JF
6160@interface SettingsView : RVPage {
6161 _transient Database *database_;
6162 NSString *name_;
6163 Package *package_;
6164 UIPreferencesTable *table_;
6165 _UISwitchSlider *subscribedSwitch_;
6166 _UISwitchSlider *ignoredSwitch_;
6167 UIPreferencesControlTableCell *subscribedCell_;
6168 UIPreferencesControlTableCell *ignoredCell_;
6169}
b4d89997 6170
807ae6d7 6171- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
b4d89997 6172
807ae6d7 6173@end
b4d89997 6174
807ae6d7 6175@implementation SettingsView
b4d89997 6176
807ae6d7
JF
6177- (void) dealloc {
6178 [table_ setDataSource:nil];
b4d89997 6179
807ae6d7
JF
6180 [name_ release];
6181 if (package_ != nil)
6182 [package_ release];
6183 [table_ release];
6184 [subscribedSwitch_ release];
6185 [ignoredSwitch_ release];
6186 [subscribedCell_ release];
6187 [ignoredCell_ release];
6188 [super dealloc];
6189}
b4d89997 6190
807ae6d7
JF
6191- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6192 if (package_ == nil)
6193 return 0;
b4d89997 6194
807ae6d7
JF
6195 return 2;
6196}
36bb2ca2 6197
807ae6d7
JF
6198- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6199 if (package_ == nil)
6200 return nil;
baf80942 6201
807ae6d7
JF
6202 switch (group) {
6203 case 0: return nil;
6204 case 1: return nil;
baf80942 6205
807ae6d7
JF
6206 default: _assert(false);
6207 }
baf80942 6208
807ae6d7 6209 return nil;
3178d79b
JF
6210}
6211
807ae6d7
JF
6212- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6213 if (package_ == nil)
6214 return NO;
baf80942 6215
807ae6d7
JF
6216 switch (group) {
6217 case 0: return NO;
6218 case 1: return YES;
b4d89997 6219
807ae6d7
JF
6220 default: _assert(false);
6221 }
3178d79b 6222
807ae6d7 6223 return NO;
dc5812ec
JF
6224}
6225
807ae6d7
JF
6226- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6227 if (package_ == nil)
6228 return 0;
dc5812ec 6229
807ae6d7
JF
6230 switch (group) {
6231 case 0: return 1;
6232 case 1: return 1;
3178d79b 6233
807ae6d7
JF
6234 default: _assert(false);
6235 }
baf80942 6236
807ae6d7 6237 return 0;
36bb2ca2 6238}
b4d89997 6239
807ae6d7
JF
6240- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6241 if (package_ == nil)
6242 return;
b4d89997 6243
807ae6d7
JF
6244 _UISwitchSlider *slider([cell control]);
6245 // XXX: this is just weird
6246 BOOL value([slider value] != 0);
6247 NSMutableDictionary *metadata([package_ metadata]);
baf80942 6248
807ae6d7
JF
6249 BOOL before;
6250 if (NSNumber *number = [metadata objectForKey:key])
6251 before = [number boolValue];
6252 else
6253 before = NO;
6254 NSLog(@"%@:%@ %@:%@ : %u:%u", cell, slider, name_, key, value, before);
36bb2ca2 6255
807ae6d7
JF
6256 if (value != before) {
6257 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6258 Changed_ = true;
6259 //[delegate_ performSelector:@selector(updateData) withObject:nil afterDelay:0];
6260 [delegate_ updateData];
6261 }
baf80942
JF
6262}
6263
807ae6d7
JF
6264- (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6265 [self onSomething:cell withKey:@"IsSubscribed"];
36bb2ca2 6266}
b4d89997 6267
807ae6d7
JF
6268- (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6269 [self onSomething:cell withKey:@"IsIgnored"];
8fe19fc1
JF
6270}
6271
807ae6d7
JF
6272- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6273 if (package_ == nil)
6274 return nil;
770f2a8e 6275
807ae6d7
JF
6276 switch (group) {
6277 case 0: switch (row) {
6278 case 0:
6279 return subscribedCell_;
6280 case 1:
6281 return ignoredCell_;
6282 default: _assert(false);
6283 } break;
6284
6285 case 1: switch (row) {
6286 case 0: {
6287 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
6288 [cell setTitle:@"Changes only shows upgrades to installed packages. This minimizes spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
6289 return cell;
6290 }
770f2a8e 6291
807ae6d7
JF
6292 default: _assert(false);
6293 } break;
770f2a8e 6294
807ae6d7
JF
6295 default: _assert(false);
6296 }
770f2a8e 6297
807ae6d7 6298 return nil;
770f2a8e
JF
6299}
6300
807ae6d7
JF
6301- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6302 if ((self = [super initWithBook:book])) {
6303 database_ = database;
6304 name_ = [package retain];
770f2a8e 6305
807ae6d7
JF
6306 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6307 [self addSubview:table_];
770f2a8e 6308
807ae6d7
JF
6309 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6310 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
770f2a8e 6311
807ae6d7
JF
6312 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6313 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
770f2a8e 6314
807ae6d7
JF
6315 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
6316 [subscribedCell_ setTitle:@"Show All Changes"];
6317 [subscribedCell_ setControl:subscribedSwitch_];
770f2a8e 6318
807ae6d7
JF
6319 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
6320 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6321 [ignoredCell_ setControl:ignoredSwitch_];
770f2a8e 6322
807ae6d7
JF
6323 [table_ setDataSource:self];
6324 [self reloadData];
6325 } return self;
6326}
770f2a8e 6327
807ae6d7
JF
6328- (void) resetViewAnimated:(BOOL)animated {
6329 [table_ resetViewAnimated:animated];
6330}
770f2a8e 6331
807ae6d7
JF
6332- (void) reloadData {
6333 if (package_ != nil)
6334 [package_ autorelease];
6335 package_ = [database_ packageWithName:name_];
6336 if (package_ != nil) {
6337 [package_ retain];
6338 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6339 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
770f2a8e 6340 }
807ae6d7
JF
6341
6342 [table_ reloadData];
770f2a8e
JF
6343}
6344
807ae6d7
JF
6345- (NSString *) title {
6346 return @"Settings";
770f2a8e
JF
6347}
6348
6349@end
6350
807ae6d7 6351/* Signature View {{{ */
770f2a8e
JF
6352@interface SignatureView : BrowserView {
6353 _transient Database *database_;
6354 NSString *package_;
6355}
6356
6357- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6358
6359@end
6360
6361@implementation SignatureView
6362
6363- (void) dealloc {
6364 [package_ release];
6365 [super dealloc];
6366}
6367
6368- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6369 // XXX: dude!
6370 [super webView:sender didClearWindowObject:window forFrame:frame];
6371}
6372
6373- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6374 if ((self = [super initWithBook:book]) != nil) {
6375 database_ = database;
6376 package_ = [package retain];
6377 [self reloadData];
6378 } return self;
6379}
6380
807ae6d7
JF
6381- (void) resetViewAnimated:(BOOL)animated {
6382}
6383
770f2a8e
JF
6384- (void) reloadData {
6385 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6386}
6387
6388@end
807ae6d7 6389/* }}} */
770f2a8e 6390
2367a917 6391@interface Cydia : UIApplication <
3178d79b 6392 ConfirmationViewDelegate,
b4d89997 6393 ProgressViewDelegate,
5f6bff8c
JF
6394 SearchViewDelegate,
6395 CydiaDelegate
2367a917 6396> {
dc5812ec 6397 UIWindow *window_;
36bb2ca2 6398
3178d79b
JF
6399 UIView *underlay_;
6400 UIView *overlay_;
36bb2ca2 6401 CYBook *book_;
a9543575 6402 UIToolbar *buttonbar_;
3178d79b 6403
affeffc7 6404 RVBook *confirm_;
9bedffaa 6405
a54b1c10 6406 NSMutableArray *essential_;
9bedffaa 6407 NSMutableArray *broken_;
dc5812ec
JF
6408
6409 Database *database_;
6410 ProgressView *progress_;
6411
06aa974d 6412 unsigned tag_;
b4d89997
JF
6413
6414 UIKeyboard *keyboard_;
bd150f54 6415 UIProgressHUD *hud_;
9b619239
JF
6416
6417 InstallView *install_;
6418 ChangesView *changes_;
6419 ManageView *manage_;
6420 SearchView *search_;
dc5812ec
JF
6421}
6422
dc5812ec
JF
6423@end
6424
6425@implementation Cydia
6426
9bedffaa
JF
6427- (void) _loaded {
6428 if ([broken_ count] != 0) {
6429 int count = [broken_ count];
6430
a9543575 6431 UIActionSheet *sheet = [[[UIActionSheet alloc]
9bedffaa
JF
6432 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6433 buttons:[NSArray arrayWithObjects:
6434 @"Forcibly Clear",
6435 @"Ignore (Temporary)",
6436 nil]
6437 defaultButtonIndex:0
6438 delegate:self
6439 context:@"fixhalf"
6440 ] autorelease];
6441
31f3cfff 6442 [sheet setBodyText:@"When the shell scripts associated with packages fail, they are left in a bad state known as either half-configured or half-installed. These errors don't go away and instead continue to cause issues. These scripts can be deleted and the packages forcibly removed."];
9bedffaa
JF
6443 [sheet popupAlertAnimated:YES];
6444 } else if (!Ignored_ && [essential_ count] != 0) {
6445 int count = [essential_ count];
6446
a9543575 6447 UIActionSheet *sheet = [[[UIActionSheet alloc]
9bedffaa 6448 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
affeffc7
JF
6449 buttons:[NSArray arrayWithObjects:
6450 @"Upgrade Essential",
6451 @"Upgrade Everything",
6452 @"Ignore (Temporary)",
6453 nil]
9bedffaa
JF
6454 defaultButtonIndex:0
6455 delegate:self
6456 context:@"upgrade"
6457 ] autorelease];
6458
31f3cfff 6459 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
9bedffaa
JF
6460 [sheet popupAlertAnimated:YES];
6461 }
6462}
6463
36bb2ca2
JF
6464- (void) _reloadData {
6465 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6466 [hud setText:@"Reloading Data"];
6467 [overlay_ addSubview:hud];
6468 [hud show:YES];*/
dc5812ec 6469
807ae6d7 6470 _trace();
dc5812ec 6471 [database_ reloadData];
807ae6d7 6472 _trace();
c25a610d 6473
36bb2ca2 6474 size_t changes(0);
9bedffaa 6475
a54b1c10 6476 [essential_ removeAllObjects];
9bedffaa 6477 [broken_ removeAllObjects];
b4d89997 6478
36bb2ca2 6479 NSArray *packages = [database_ packages];
affeffc7 6480 for (Package *package in packages) {
9bedffaa
JF
6481 if ([package half])
6482 [broken_ addObject:package];
31f3cfff 6483 if ([package upgradableAndEssential:NO]) {
a54b1c10
JF
6484 if ([package essential])
6485 [essential_ addObject:package];
36bb2ca2 6486 ++changes;
a54b1c10 6487 }
36bb2ca2 6488 }
b4d89997 6489
36bb2ca2
JF
6490 if (changes != 0) {
6491 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
c25a610d 6492 [buttonbar_ setBadgeValue:badge forButton:3];
9e98e020
JF
6493 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6494 [buttonbar_ setBadgeAnimated:YES forButton:3];
c25a610d
JF
6495 [self setApplicationBadge:badge];
6496 } else {
6497 [buttonbar_ setBadgeValue:nil forButton:3];
9e98e020
JF
6498 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6499 [buttonbar_ setBadgeAnimated:NO forButton:3];
c25a610d
JF
6500 [self removeApplicationBadge];
6501 }
b4d89997 6502
6d9712c4
JF
6503 [self updateData];
6504
c390d3ab 6505#if !ForSaurik
6f1a15d9
JF
6506 if ([packages count] == 0);
6507 else if (Loaded_)
c390d3ab 6508#endif
6d9712c4 6509 [self _loaded];
c390d3ab 6510#if !ForSaurik
6f1a15d9 6511 else {
6d9712c4
JF
6512 Loaded_ = YES;
6513 [book_ update];
6f1a15d9 6514 }
c390d3ab 6515#endif
6d9712c4
JF
6516
6517 /*[hud show:NO];
6518 [hud removeFromSuperview];*/
6519}
6520
7b0ce2da 6521- (void) _saveConfig {
bbb879fb 6522 if (Changed_) {
807ae6d7 6523 _trace();
bbb879fb 6524 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
807ae6d7 6525 _trace();
bbb879fb
JF
6526 Changed_ = false;
6527 }
7b0ce2da
JF
6528}
6529
6530- (void) updateData {
6531 [self _saveConfig];
36bb2ca2 6532
9b619239 6533 /* XXX: this is just stupid */
807ae6d7 6534 if (tag_ != 2 && install_ != nil)
9b619239 6535 [install_ reloadData];
807ae6d7 6536 if (tag_ != 3 && changes_ != nil)
9b619239 6537 [changes_ reloadData];
807ae6d7 6538 if (tag_ != 5 && search_ != nil)
9b619239
JF
6539 [search_ reloadData];
6540
36bb2ca2 6541 [book_ reloadData];
b4d89997
JF
6542}
6543
7b0ce2da
JF
6544- (void) update_ {
6545 [database_ update];
6546}
6547
6548- (void) syncData {
6549 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6550 _assert(file != NULL);
6551
6552 NSArray *keys = [Sources_ allKeys];
6553
6554 for (int i(0), e([keys count]); i != e; ++i) {
6555 NSString *key = [keys objectAtIndex:i];
6556 NSDictionary *source = [Sources_ objectForKey:key];
6557
6558 fprintf(file, "%s %s %s\n",
6559 [[source objectForKey:@"Type"] UTF8String],
6560 [[source objectForKey:@"URI"] UTF8String],
6561 [[source objectForKey:@"Distribution"] UTF8String]
6562 );
6563 }
6564
6565 fclose(file);
6566
6567 [self _saveConfig];
6568
6569 [progress_
6570 detachNewThreadSelector:@selector(update_)
6571 toTarget:self
6572 withObject:nil
baf80942 6573 title:@"Updating Sources"
7b0ce2da
JF
6574 ];
6575}
6576
36bb2ca2
JF
6577- (void) reloadData {
6578 @synchronized (self) {
6579 if (confirm_ == nil)
6580 [self _reloadData];
6581 }
b4d89997
JF
6582}
6583
6584- (void) resolve {
6585 pkgProblemResolver *resolver = [database_ resolver];
6586
6587 resolver->InstallProtect();
6588 if (!resolver->Resolve(true))
6589 _error->Discard();
2367a917
JF
6590}
6591
6592- (void) perform {
8fe19fc1 6593 [database_ prepare];
49048579 6594
affeffc7
JF
6595 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6596 [confirm_ setDelegate:self];
49048579 6597
affeffc7
JF
6598 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6599 [page setDelegate:self];
49048579 6600
affeffc7
JF
6601 [confirm_ setPage:page];
6602 [underlay_ popSubview:confirm_];
3178d79b
JF
6603}
6604
36bb2ca2
JF
6605- (void) installPackage:(Package *)package {
6606 @synchronized (self) {
6607 [package install];
6608 [self resolve];
6609 [self perform];
6610 }
6611}
6612
6613- (void) removePackage:(Package *)package {
6614 @synchronized (self) {
6615 [package remove];
6616 [self resolve];
6617 [self perform];
6618 }
6619}
6620
6621- (void) distUpgrade {
6622 @synchronized (self) {
6623 [database_ upgrade];
6624 [self perform];
6625 }
b4d89997
JF
6626}
6627
3178d79b 6628- (void) cancel {
36bb2ca2 6629 @synchronized (self) {
36bb2ca2 6630 [self _reloadData];
c390d3ab
JF
6631 if (confirm_ != nil) {
6632 [confirm_ release];
6633 confirm_ = nil;
6634 }
36bb2ca2 6635 }
3178d79b
JF
6636}
6637
6638- (void) confirm {
6639 [overlay_ removeFromSuperview];
77fcccaf 6640 reload_ = true;
3178d79b 6641
2367a917
JF
6642 [progress_
6643 detachNewThreadSelector:@selector(perform)
6644 toTarget:database_
6645 withObject:nil
baf80942 6646 title:@"Running"
2367a917
JF
6647 ];
6648}
6649
b31b87cc
JF
6650- (void) bootstrap_ {
6651 [database_ update];
6652 [database_ upgrade];
6653 [database_ prepare];
6654 [database_ perform];
6655}
6656
6657- (void) bootstrap {
6658 [progress_
6659 detachNewThreadSelector:@selector(bootstrap_)
6660 toTarget:self
6661 withObject:nil
baf80942 6662 title:@"Bootstrap Install"
b31b87cc
JF
6663 ];
6664}
6665
2367a917 6666- (void) progressViewIsComplete:(ProgressView *)progress {
c390d3ab
JF
6667 if (confirm_ != nil) {
6668 [underlay_ addSubview:overlay_];
affeffc7 6669 [confirm_ popFromSuperviewAnimated:NO];
3178d79b 6670 }
c390d3ab
JF
6671
6672 [self cancel];
dc5812ec
JF
6673}
6674
36bb2ca2 6675- (void) setPage:(RVPage *)page {
9b619239 6676 [page resetViewAnimated:NO];
36bb2ca2
JF
6677 [page setDelegate:self];
6678 [book_ setPage:page];
dc5812ec
JF
6679}
6680
7b0ce2da 6681- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
770f2a8e 6682 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
7b0ce2da 6683 [browser loadURL:url];
36bb2ca2 6684 return browser;
2367a917
JF
6685}
6686
7b0ce2da
JF
6687- (void) _setHomePage {
6688 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6689}
6690
dc5812ec 6691- (void) buttonBarItemTapped:(id)sender {
06aa974d 6692 unsigned tag = [sender tag];
559dec34
JF
6693 if (tag == tag_) {
6694 [book_ resetViewAnimated:YES];
6695 return;
6d9712c4
JF
6696 } else if (tag_ == 2 && tag != 2)
6697 [install_ resetView];
dc5812ec 6698
06aa974d 6699 switch (tag) {
11108eb6 6700 case 1: [self _setHomePage]; break;
36bb2ca2 6701
11108eb6
JF
6702 case 2: [self setPage:install_]; break;
6703 case 3: [self setPage:changes_]; break;
6704 case 4: [self setPage:manage_]; break;
6705 case 5: [self setPage:search_]; break;
36bb2ca2 6706
11108eb6 6707 default: _assert(false);
dc5812ec
JF
6708 }
6709
06aa974d 6710 tag_ = tag;
06aa974d
JF
6711}
6712
b4d89997 6713- (void) applicationWillSuspend {
77fcccaf 6714 [database_ clean];
eeb9b112 6715 [super applicationWillSuspend];
b4d89997
JF
6716}
6717
7b0ce2da 6718- (void) askForSettings {
a9543575 6719 UIActionSheet *role = [[[UIActionSheet alloc]
7b0ce2da
JF
6720 initWithTitle:@"Who Are You?"
6721 buttons:[NSArray arrayWithObjects:
6722 @"User (Graphical Only)",
f641a0e5 6723 @"Hacker (+ Command Line)",
7b0ce2da
JF
6724 @"Developer (No Filters)",
6725 nil]
6726 defaultButtonIndex:-1
6727 delegate:self
6728 context:@"role"
6729 ] autorelease];
6730
6731 [role setBodyText:@"Not all of the packages available via Cydia are designed to be used by all users. Please categorize yourself so that Cydia can apply helpful filters.\n\nThis choice can be changed from \"Settings\" under the \"Manage\" tab."];
6732 [role popupAlertAnimated:YES];
6733}
6734
bd150f54
JF
6735- (void) finish {
6736 if (hud_ != nil) {
6737 [self setStatusBarShowsProgress:NO];
a54b1c10 6738
bd150f54
JF
6739 [hud_ show:NO];
6740 [hud_ removeFromSuperview];
6741 [hud_ autorelease];
6742 hud_ = nil;
f9d1e9a0 6743
c6ad32a6
JF
6744 pid_t pid = ExecFork();
6745 if (pid == 0) {
6746 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6747 perror("launchctl stop");
6748 }
6749
f9d1e9a0 6750 return;
bd150f54 6751 }
3178d79b 6752
7b0ce2da
JF
6753 if (Role_ == nil) {
6754 [self askForSettings];
6755 return;
6756 }
6757
3178d79b 6758 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
b31b87cc 6759
bd150f54 6760 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
36bb2ca2
JF
6761 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6762 0, 0, screenrect.size.width, screenrect.size.height - 48
6763 ) database:database_];
dc5812ec 6764
36bb2ca2 6765 [book_ setDelegate:self];
dc5812ec 6766
36bb2ca2 6767 [overlay_ addSubview:book_];
b19871dd 6768
dc5812ec
JF
6769 NSArray *buttonitems = [NSArray arrayWithObjects:
6770 [NSDictionary dictionaryWithObjectsAndKeys:
6771 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
9b619239
JF
6772 @"home-up.png", kUIButtonBarButtonInfo,
6773 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6774 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6775 self, kUIButtonBarButtonTarget,
9b619239 6776 @"Home", kUIButtonBarButtonTitle,
dc5812ec
JF
6777 @"0", kUIButtonBarButtonType,
6778 nil],
6779
6780 [NSDictionary dictionaryWithObjectsAndKeys:
6781 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6782 @"install-up.png", kUIButtonBarButtonInfo,
6783 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6784 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6785 self, kUIButtonBarButtonTarget,
6d9712c4 6786 @"Sections", kUIButtonBarButtonTitle,
dc5812ec
JF
6787 @"0", kUIButtonBarButtonType,
6788 nil],
6789
6790 [NSDictionary dictionaryWithObjectsAndKeys:
6791 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
6792 @"changes-up.png", kUIButtonBarButtonInfo,
6793 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6794 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6795 self, kUIButtonBarButtonTarget,
b4d89997 6796 @"Changes", kUIButtonBarButtonTitle,
dc5812ec
JF
6797 @"0", kUIButtonBarButtonType,
6798 nil],
6799
6800 [NSDictionary dictionaryWithObjectsAndKeys:
6801 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
6802 @"manage-up.png", kUIButtonBarButtonInfo,
6803 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6804 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6805 self, kUIButtonBarButtonTarget,
36bb2ca2 6806 @"Manage", kUIButtonBarButtonTitle,
dc5812ec
JF
6807 @"0", kUIButtonBarButtonType,
6808 nil],
6809
6810 [NSDictionary dictionaryWithObjectsAndKeys:
6811 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
6812 @"search-up.png", kUIButtonBarButtonInfo,
6813 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6814 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6815 self, kUIButtonBarButtonTarget,
b4d89997 6816 @"Search", kUIButtonBarButtonTitle,
dc5812ec
JF
6817 @"0", kUIButtonBarButtonType,
6818 nil],
6819 nil];
6820
a9543575 6821 buttonbar_ = [[UIToolbar alloc]
3178d79b 6822 initInView:overlay_
dc5812ec 6823 withFrame:CGRectMake(
2388b078
JF
6824 0, screenrect.size.height - ButtonBarHeight_,
6825 screenrect.size.width, ButtonBarHeight_
dc5812ec
JF
6826 )
6827 withItemList:buttonitems
6828 ];
6829
2367a917
JF
6830 [buttonbar_ setDelegate:self];
6831 [buttonbar_ setBarStyle:1];
6832 [buttonbar_ setButtonBarTrackingMode:2];
dc5812ec
JF
6833
6834 int buttons[5] = {1, 2, 3, 4, 5};
2367a917
JF
6835 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
6836 [buttonbar_ showButtonGroup:0 withDuration:0];
dc5812ec
JF
6837
6838 for (int i = 0; i != 5; ++i)
2367a917 6839 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
2388b078 6840 i * 64 + 2, 1, 60, ButtonBarHeight_
dc5812ec
JF
6841 )];
6842
2367a917 6843 [buttonbar_ showSelectionForButton:1];
3178d79b 6844 [overlay_ addSubview:buttonbar_];
dc5812ec 6845
b4d89997 6846 [UIKeyboard initImplementationNow];
b4d89997 6847 CGSize keysize = [UIKeyboard defaultSize];
ab398adf 6848 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
b4d89997 6849 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
d73cede2 6850 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
ab398adf 6851 [overlay_ addSubview:keyboard_];
b4d89997 6852
807ae6d7
JF
6853 if (!bootstrap_)
6854 [underlay_ addSubview:overlay_];
6855
6856 [self reloadData];
6857
9b619239
JF
6858 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
6859 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
9b619239
JF
6860 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
6861
7b0ce2da
JF
6862 manage_ = (ManageView *) [[self
6863 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
6864 withClass:[ManageView class]
6865 ] retain];
6866
b31b87cc
JF
6867 if (bootstrap_)
6868 [self bootstrap];
36bb2ca2 6869 else
9b619239 6870 [self _setHomePage];
b4d89997
JF
6871}
6872
a9543575 6873- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
7b0ce2da
JF
6874 NSString *context = [sheet context];
6875 if ([context isEqualToString:@"fixhalf"])
6876 switch (button) {
6877 case 1:
6878 @synchronized (self) {
6879 for (int i = 0, e = [broken_ count]; i != e; ++i) {
6880 Package *broken = [broken_ objectAtIndex:i];
6881 [broken remove];
6882
6883 NSString *id = [broken id];
6884 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
6885 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
6886 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
6887 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
6888 }
6889
6890 [self resolve];
6891 [self perform];
6892 }
6893 break;
6894
6895 case 2:
6896 [broken_ removeAllObjects];
6897 [self _loaded];
6898 break;
6899
6900 default:
6901 _assert(false);
6902 }
6903 else if ([context isEqualToString:@"role"]) {
6904 switch (button) {
6905 case 1: Role_ = @"User"; break;
6906 case 2: Role_ = @"Hacker"; break;
6907 case 3: Role_ = @"Developer"; break;
6908
6909 default:
6910 Role_ = nil;
6911 _assert(false);
6912 }
6913
6914 bool reset = Settings_ != nil;
6915
6916 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6917 Role_, @"Role",
6918 nil];
6919
6920 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6921
6922 Changed_ = true;
6923
6924 if (reset)
6925 [self updateData];
6926 else
6927 [self finish];
6928 } else if ([context isEqualToString:@"upgrade"])
6929 switch (button) {
6930 case 1:
6931 @synchronized (self) {
6932 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6933 Package *essential = [essential_ objectAtIndex:i];
6934 [essential install];
6935 }
6936
6937 [self resolve];
6938 [self perform];
6939 }
6940 break;
6941
6942 case 2:
affeffc7
JF
6943 [self distUpgrade];
6944 break;
6945
6946 case 3:
7b0ce2da
JF
6947 Ignored_ = YES;
6948 break;
6949
6950 default:
6951 _assert(false);
6952 }
6953
6954 [sheet dismiss];
6955}
6956
affeffc7 6957- (void) reorganize { _pooled
e64882de
JF
6958 system("/usr/libexec/cydia/free.sh");
6959 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
bd150f54
JF
6960}
6961
6962- (void) applicationSuspend:(__GSEvent *)event {
6963 if (hud_ == nil && ![progress_ isRunning])
6964 [super applicationSuspend:event];
bd150f54
JF
6965}
6966
6d9712c4
JF
6967- (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6968 if (hud_ == nil)
6969 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6970}
6971
6972- (void) _setSuspended:(BOOL)value {
6973 if (hud_ == nil)
6974 [super _setSuspended:value];
6975}
6976
7b0ce2da
JF
6977- (UIProgressHUD *) addProgressHUD {
6978 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6979 [hud show:YES];
6980 [underlay_ addSubview:hud];
6981 return hud;
6982}
6983
c390d3ab 6984- (void) openMailToURL:(NSURL *)url {
86316a91
JF
6985// XXX: this makes me sad
6986#if 0
c390d3ab 6987 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
86316a91
JF
6988#else
6989 [UIApp openURL:url];
6990#endif
c390d3ab
JF
6991}
6992
6993- (RVPage *) pageForPackage:(NSString *)name {
6994 if (Package *package = [database_ packageWithName:name]) {
6995 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6996 [view setPackage:package];
6997 return view;
6998 } else {
6999 UIActionSheet *sheet = [[[UIActionSheet alloc]
7000 initWithTitle:@"Cannot Locate Package"
7001 buttons:[NSArray arrayWithObjects:@"Close", nil]
7002 defaultButtonIndex:0
7003 delegate:self
7004 context:@"missing"
7005 ] autorelease];
7006
7007 [sheet setBodyText:[NSString stringWithFormat:
7008 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7009 , name]];
7010
7011 [sheet popupAlertAnimated:YES];
7012 return nil;
7013 }
7014}
7015
7016- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7017 NSString *href = [url absoluteString];
7018
7019 if (tag != NULL)
7020 tag = 0;
7021
7022 if ([href isEqualToString:@"cydia://add-source"])
7023 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7024 else if ([href isEqualToString:@"cydia://sources"])
7025 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7026 else if ([href isEqualToString:@"cydia://packages"])
7027 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7028 else if ([href hasPrefix:@"cydia://url/"])
7029 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
affeffc7
JF
7030 else if ([href hasPrefix:@"cydia://launch/"])
7031 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
807ae6d7
JF
7032 else if ([href hasPrefix:@"cydia://package-settings/"])
7033 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
770f2a8e
JF
7034 else if ([href hasPrefix:@"cydia://package-signature/"])
7035 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
c390d3ab
JF
7036 else if ([href hasPrefix:@"cydia://package/"])
7037 return [self pageForPackage:[href substringFromIndex:16]];
7038 else if ([href hasPrefix:@"cydia://files/"]) {
7039 NSString *name = [href substringFromIndex:14];
7040
7041 if (Package *package = [database_ packageWithName:name]) {
7042 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7043 [files setPackage:package];
7044 return files;
7045 }
7046 }
7047
7048 return nil;
7049}
7050
7051- (void) applicationOpenURL:(NSURL *)url {
7052 [super applicationOpenURL:url];
7053 int tag;
7054 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7055 [self setPage:page];
7056 [buttonbar_ showSelectionForButton:tag];
7057 tag_ = tag;
7058 }
7059}
7060
bd150f54 7061- (void) applicationDidFinishLaunching:(id)unused {
f641a0e5
JF
7062 Font12_ = [[UIFont systemFontOfSize:12] retain];
7063 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7064 Font14_ = [[UIFont systemFontOfSize:14] retain];
7065 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7066 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7067
bd150f54
JF
7068 _assert(pkgInitConfig(*_config));
7069 _assert(pkgInitSystem(*_config, _system));
7070
bd150f54
JF
7071 tag_ = 1;
7072
7073 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7074 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7075
770f2a8e
JF
7076 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7077
bd150f54
JF
7078 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7079 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7080
f641a0e5
JF
7081 [window_ orderFront:self];
7082 [window_ makeKey:self];
c390d3ab 7083 [window_ setHidden:NO];
bd150f54 7084
770f2a8e 7085 database_ = [Database sharedInstance];
bd150f54
JF
7086 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7087 [database_ setDelegate:progress_];
7088 [window_ setContentView:progress_];
7089
7090 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7091 [progress_ setContentView:underlay_];
7092
7093 [progress_ resetView];
7094
6d9712c4 7095 if (
bd150f54 7096 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
bd150f54 7097 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6d9712c4
JF
7098 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7099 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7100 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
90515f88
JF
7101 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7102 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
bd150f54 7103 ) {
5afbb4b7
JF
7104 [self setIdleTimerDisabled:YES];
7105
7b0ce2da 7106 hud_ = [self addProgressHUD];
c6ad32a6 7107 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
bd150f54
JF
7108
7109 [self setStatusBarShowsProgress:YES];
7110
7111 [NSThread
7112 detachNewThreadSelector:@selector(reorganize)
7113 toTarget:self
7114 withObject:nil
7115 ];
6d9712c4 7116 } else
bd150f54
JF
7117 [self finish];
7118}
7119
affeffc7 7120/* Web Scripting {{{ */
c390d3ab
JF
7121+ (NSString *) webScriptNameForSelector:(SEL)selector {
7122 if (selector == @selector(supports:))
7123 return @"supports";
7124 return nil;
7125}
7126
c390d3ab 7127+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
c390d3ab
JF
7128 return selector != @selector(supports:);
7129}
7130
affeffc7
JF
7131- (BOOL) supports:(NSString *)feature {
7132 return [feature isEqualToString:@"window.open"];
7133}
7134/* }}} */
7135
b4d89997 7136- (void) showKeyboard:(BOOL)show {
ab398adf
JF
7137 CGSize keysize = [UIKeyboard defaultSize];
7138 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7139 CGRect keyup = keydown;
7140 keyup.origin.y -= keysize.height;
7141
7142 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7143 [animation setSignificantRectFields:2];
7144
7145 if (show) {
7146 [animation setStartFrame:keydown];
7147 [animation setEndFrame:keyup];
7148 [keyboard_ activate];
7149 } else {
7150 [animation setStartFrame:keyup];
7151 [animation setEndFrame:keydown];
7152 [keyboard_ deactivate];
7153 }
7154
7155 [[UIAnimator sharedAnimator]
7156 addAnimations:[NSArray arrayWithObjects:animation, nil]
2388b078 7157 withDuration:KeyboardTime_
ab398adf
JF
7158 start:YES
7159 ];
dc5812ec
JF
7160}
7161
a9543575 7162- (void) slideUp:(UIActionSheet *)alert {
77fcccaf
JF
7163 if (Advanced_)
7164 [alert presentSheetFromButtonBar:buttonbar_];
7165 else
7166 [alert presentSheetInView:overlay_];
36bb2ca2
JF
7167}
7168
dc5812ec
JF
7169@end
7170
affeffc7 7171void AddPreferences(NSString *plist) { _pooled
b4d89997
JF
7172 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7173 _assert(settings != NULL);
7174 NSMutableArray *items = [settings objectForKey:@"items"];
7175
7176 bool cydia(false);
7177
7178 for (size_t i(0); i != [items count]; ++i) {
7179 NSMutableDictionary *item([items objectAtIndex:i]);
7180 NSString *label = [item objectForKey:@"label"];
7181 if (label != nil && [label isEqualToString:@"Cydia"]) {
7182 cydia = true;
7183 break;
7184 }
7185 }
7186
7187 if (!cydia) {
7188 for (size_t i(0); i != [items count]; ++i) {
7189 NSDictionary *item([items objectAtIndex:i]);
7190 NSString *label = [item objectForKey:@"label"];
7191 if (label != nil && [label isEqualToString:@"General"]) {
7192 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7193 @"CydiaSettings", @"bundle",
7194 @"PSLinkCell", @"cell",
7195 [NSNumber numberWithBool:YES], @"hasIcon",
7196 [NSNumber numberWithBool:YES], @"isController",
7197 @"Cydia", @"label",
7198 nil] atIndex:(i + 1)];
7199
7200 break;
7201 }
7202 }
7203
7204 _assert([settings writeToFile:plist atomically:YES] == YES);
7205 }
b4d89997
JF
7206}
7207
7208/*IMP alloc_;
7209id Alloc_(id self, SEL selector) {
7210 id object = alloc_(self, selector);
c390d3ab 7211 lprintf("[%s]A-%p\n", self->isa->name, object);
b4d89997
JF
7212 return object;
7213}*/
7214
36bb2ca2
JF
7215/*IMP dealloc_;
7216id Dealloc_(id self, SEL selector) {
7217 id object = dealloc_(self, selector);
c390d3ab 7218 lprintf("[%s]D-%p\n", self->isa->name, object);
36bb2ca2
JF
7219 return object;
7220}*/
b4d89997 7221
affeffc7 7222int main(int argc, char *argv[]) { _pooled
b31b87cc
JF
7223 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
7224
7d2ac47f 7225 App_ = [[NSBundle mainBundle] bundlePath];
d73cede2 7226 Home_ = NSHomeDirectory();
87f46a96 7227 Locale_ = CFLocaleCopyCurrent();
d73cede2
JF
7228
7229 {
7230 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7231 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7232 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7233 Sounds_Keyboard_ = [keyboard boolValue];
7234 }
7235
b4d89997
JF
7236 setuid(0);
7237 setgid(0);
7238
aa629131
JF
7239 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7240 _assert(errno == ENOENT);
7241 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7242 _assert(errno == ENOENT);
aa5e5990 7243
b4d89997
JF
7244 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7245 alloc_ = alloc->method_imp;
7246 alloc->method_imp = (IMP) &Alloc_;*/
7247
36bb2ca2
JF
7248 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7249 dealloc_ = dealloc->method_imp;
7250 dealloc->method_imp = (IMP) &Dealloc_;*/
7251
3178d79b 7252 size_t size;
c390d3ab
JF
7253
7254 int maxproc;
7255 size = sizeof(maxproc);
7256 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7257 perror("sysctlbyname(\"kern.maxproc\", ?)");
7258 else if (maxproc < 64) {
7259 maxproc = 64;
7260 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7261 perror("sysctlbyname(\"kern.maxproc\", #)");
7262 }
7263
3178d79b
JF
7264 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7265 char *machine = new char[size];
c390d3ab
JF
7266 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7267 perror("sysctlbyname(\"hw.machine\", ?)");
7268 else
7269 Machine_ = machine;
3178d79b 7270
87f46a96 7271 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
3178d79b 7272
99364ec4
JF
7273 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7274 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
b4d89997
JF
7275
7276 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7277 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6d9712c4 7278 else {
7b0ce2da
JF
7279 Settings_ = [Metadata_ objectForKey:@"Settings"];
7280
b4d89997 7281 Packages_ = [Metadata_ objectForKey:@"Packages"];
6d9712c4 7282 Sections_ = [Metadata_ objectForKey:@"Sections"];
7b0ce2da
JF
7283 Sources_ = [Metadata_ objectForKey:@"Sources"];
7284 }
7285
7286 if (Settings_ != nil)
7287 Role_ = [Settings_ objectForKey:@"Role"];
7288
7289 if (Packages_ == nil) {
7290 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7291 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7292 }
7293
7294 if (Sections_ == nil) {
7295 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7296 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7297 }
7298
7299 if (Sources_ == nil) {
7300 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7301 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6d9712c4 7302 }
b4d89997 7303
affeffc7 7304#if RecycleWebViews
baf80942 7305 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
affeffc7 7306#endif
baf80942 7307
dddbc481
JF
7308 if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
7309 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7310
26c2dd8c
JF
7311 if (access("/User", F_OK) != 0)
7312 system("/usr/libexec/cydia/firmware.sh");
9e98e020 7313
87f46a96
JF
7314 _assert([[NSFileManager defaultManager]
7315 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7316 withIntermediateDirectories:YES
7317 attributes:nil
7318 error:NULL
7319 ]);
7320
36bb2ca2
JF
7321 space_ = CGColorSpaceCreateDeviceRGB();
7322
f641a0e5 7323 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
77fcccaf 7324 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
36bb2ca2 7325 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
baf80942 7326 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
36bb2ca2 7327 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7b0ce2da
JF
7328 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7329
bc8cd583
JF
7330 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7331
46dbfd32 7332 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7b0ce2da 7333
87f46a96
JF
7334 UIApplicationUseLegacyEvents(YES);
7335 UIKeyboardDisableAutomaticAppearance();
7336
a9543575 7337 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
36bb2ca2
JF
7338
7339 CGColorSpaceRelease(space_);
199d0ba5 7340 CFRelease(Locale_);
36bb2ca2 7341
36bb2ca2 7342 return value;
dc5812ec 7343}