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