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