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