]> git.saurik.com Git - cydia.git/blame - Cydia.mm
URL Authentication.
[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
1cedb821 264#define ForRelease 0
9487f027 265#define ForSaurik 1 && !ForRelease
affeffc7 266#define RecycleWebViews 0
1cedb821 267#define AlwaysReload 0 && !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
JF
4464 [sheet dismiss];
4465 }
7b0ce2da
JF
4466}
4467
4468- (id) initWithBook:(RVBook *)book database:(Database *)database {
4469 if ((self = [super initWithBook:book]) != nil) {
4470 database_ = database;
4471 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4472
4473 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4474 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4475 [list_ setShouldHideHeaderInShortLists:NO];
4476
4477 [self addSubview:list_];
4478 [list_ setDataSource:self];
4479
4480 UITableColumn *column = [[UITableColumn alloc]
4481 initWithTitle:@"Name"
4482 identifier:@"name"
4483 width:[self frame].size.width
4484 ];
4485
4486 UITable *table = [list_ table];
4487 [table setSeparatorStyle:1];
4488 [table addTableColumn:column];
4489 [table setDelegate:self];
4490
4491 [self reloadData];
baf80942
JF
4492
4493 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4494 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
7b0ce2da
JF
4495 } return self;
4496}
4497
4498- (void) reloadData {
4499 pkgSourceList list;
4500 _assert(list.ReadMainList());
4501
4502 [sources_ removeAllObjects];
4503 [sources_ addObjectsFromArray:[database_ sources]];
807ae6d7 4504 _trace();
7b0ce2da 4505 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
807ae6d7 4506 _trace();
7b0ce2da
JF
4507
4508 int count = [sources_ count];
4509 for (offset_ = 0; offset_ != count; ++offset_) {
4510 Source *source = [sources_ objectAtIndex:offset_];
4511 if ([source record] == nil)
4512 break;
4513 }
4514
4515 [list_ reloadData];
4516}
4517
4518- (void) resetViewAnimated:(BOOL)animated {
4519 [list_ resetViewAnimated:animated];
4520}
4521
4522- (void) _leftButtonClicked {
4523 /*[book_ pushPage:[[[AddSourceView alloc]
4524 initWithBook:book_
4525 database:database_
4526 ] autorelease]];*/
4527
a9543575 4528 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4529 initWithTitle:@"Enter Cydia/APT URL"
4530 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4531 defaultButtonIndex:0
4532 delegate:self
4533 context:@"source"
4534 ] autorelease];
4535
1cedb821
JF
4536 [sheet setNumberOfRows:1];
4537
7b0ce2da
JF
4538 [sheet addTextFieldWithValue:@"http://" label:@""];
4539
a9543575
JF
4540 UITextInputTraits *traits = [[sheet textField] textInputTraits];
4541 [traits setAutocapitalizationType:0];
1cedb821 4542 [traits setKeyboardType:UIKeyboardTypeURL];
a9543575 4543 [traits setAutocorrectionType:1];
7b0ce2da
JF
4544
4545 [sheet popupAlertAnimated:YES];
4546}
4547
4548- (void) _rightButtonClicked {
4549 UITable *table = [list_ table];
4550 BOOL editing = [table isRowDeletionEnabled];
4551 [table enableRowDeletion:!editing animated:YES];
4552 [book_ reloadButtonsForPage:self];
4553}
4554
4555- (NSString *) title {
4556 return @"Sources";
4557}
4558
7b0ce2da
JF
4559- (NSString *) leftButtonTitle {
4560 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4561}
4562
4563- (NSString *) rightButtonTitle {
4564 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4565}
4566
baf80942
JF
4567- (UINavigationButtonStyle) rightButtonStyle {
4568 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
7b0ce2da
JF
4569}
4570
4571@end
4572/* }}} */
4573
4574/* Installed View {{{ */
4575@interface InstalledView : RVPage {
4576 _transient Database *database_;
4577 PackageTable *packages_;
f641a0e5 4578 BOOL expert_;
7b0ce2da
JF
4579}
4580
4581- (id) initWithBook:(RVBook *)book database:(Database *)database;
4582
4583@end
4584
4585@implementation InstalledView
4586
4587- (void) dealloc {
4588 [packages_ release];
4589 [super dealloc];
4590}
4591
4592- (id) initWithBook:(RVBook *)book database:(Database *)database {
4593 if ((self = [super initWithBook:book]) != nil) {
4594 database_ = database;
4595
4596 packages_ = [[PackageTable alloc]
4597 initWithBook:book
4598 database:database
4599 title:nil
440c215d 4600 filter:@selector(isInstalledAndVisible:)
f641a0e5 4601 with:[NSNumber numberWithBool:YES]
7b0ce2da
JF
4602 ];
4603
4604 [self addSubview:packages_];
baf80942
JF
4605
4606 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4607 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
7b0ce2da
JF
4608 } return self;
4609}
4610
4611- (void) resetViewAnimated:(BOOL)animated {
4612 [packages_ resetViewAnimated:animated];
4613}
4614
4615- (void) reloadData {
4616 [packages_ reloadData];
4617}
4618
f641a0e5
JF
4619- (void) _rightButtonClicked {
4620 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4621 [packages_ reloadData];
4622 expert_ = !expert_;
4623 [book_ reloadButtonsForPage:self];
4624}
4625
7b0ce2da 4626- (NSString *) title {
f641a0e5 4627 return @"Installed";
7b0ce2da
JF
4628}
4629
4630- (NSString *) backButtonTitle {
4631 return @"Packages";
4632}
4633
f641a0e5
JF
4634- (NSString *) rightButtonTitle {
4635 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4636}
4637
baf80942
JF
4638- (UINavigationButtonStyle) rightButtonStyle {
4639 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
f641a0e5
JF
4640}
4641
7b0ce2da
JF
4642- (void) setDelegate:(id)delegate {
4643 [super setDelegate:delegate];
4644 [packages_ setDelegate:delegate];
4645}
4646
4647@end
4648/* }}} */
4649
98228790 4650/* Home View {{{ */
7b0ce2da
JF
4651@interface HomeView : BrowserView {
4652}
4653
4654@end
4655
4656@implementation HomeView
4657
a9543575 4658- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821
JF
4659 NSString *context([sheet context]);
4660
4661 if ([context isEqualToString:@"about"])
4662 [sheet dismiss];
4663 else
4664 [super alertSheet:sheet buttonClicked:button];
7b0ce2da
JF
4665}
4666
4667- (void) _leftButtonClicked {
a9543575 4668 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4669 initWithTitle:@"About Cydia Installer"
4670 buttons:[NSArray arrayWithObjects:@"Close", nil]
4671 defaultButtonIndex:0
4672 delegate:self
4673 context:@"about"
4674 ] autorelease];
4675
4676 [sheet setBodyText:
4677 @"Copyright (C) 2008\n"
4678 "Jay Freeman (saurik)\n"
4679 "saurik@saurik.com\n"
4680 "http://www.saurik.com/\n"
4681 "\n"
4682 "The Okori Group\n"
4683 "http://www.theokorigroup.com/\n"
4684 "\n"
4685 "College of Creative Studies,\n"
4686 "University of California,\n"
4687 "Santa Barbara\n"
4688 "http://www.ccs.ucsb.edu/"
4689 ];
4690
4691 [sheet popupAlertAnimated:YES];
4692}
4693
4694- (NSString *) leftButtonTitle {
4695 return @"About";
4696}
4697
4698@end
98228790
JF
4699/* }}} */
4700/* Manage View {{{ */
7b0ce2da
JF
4701@interface ManageView : BrowserView {
4702}
4703
4704@end
4705
4706@implementation ManageView
4707
4708- (NSString *) title {
4709 return @"Manage";
4710}
4711
4712- (void) _leftButtonClicked {
4713 [delegate_ askForSettings];
4714}
4715
4716- (NSString *) leftButtonTitle {
4717 return @"Settings";
4718}
4719
bf8476c8 4720#if !AlwaysReload
6f1a15d9 4721- (NSString *) _rightButtonTitle {
7b0ce2da
JF
4722 return nil;
4723}
bf8476c8 4724#endif
7b0ce2da
JF
4725
4726@end
98228790 4727/* }}} */
7b0ce2da 4728
c390d3ab
JF
4729@interface WebView (Cydia)
4730- (void) setScriptDebugDelegate:(id)delegate;
4731- (void) _setFormDelegate:(id)delegate;
4732- (void) _setUIKitDelegate:(id)delegate;
4733- (void) setWebMailDelegate:(id)delegate;
4734- (void) _setLayoutInterval:(float)interval;
4735@end
4736
baf80942
JF
4737/* Indirect Delegate {{{ */
4738@interface IndirectDelegate : NSProxy {
c390d3ab 4739 _transient volatile id delegate_;
baf80942
JF
4740}
4741
4742- (void) setDelegate:(id)delegate;
4743- (id) initWithDelegate:(id)delegate;
4744@end
4745
4746@implementation IndirectDelegate
4747
4748- (void) setDelegate:(id)delegate {
4749 delegate_ = delegate;
4750}
4751
4752- (id) initWithDelegate:(id)delegate {
4753 delegate_ = delegate;
4754 return self;
4755}
4756
4757- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4758 if (delegate_ != nil)
4759 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4760 return sig;
c390d3ab
JF
4761 // XXX: I fucking hate Apple so very very bad
4762 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
baf80942
JF
4763}
4764
c390d3ab 4765- (void) forwardInvocation:(NSInvocation *)inv {
baf80942
JF
4766 SEL sel = [inv selector];
4767 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4768 [inv invokeWithTarget:delegate_];
4769}
4770
4771@end
4772/* }}} */
7b0ce2da
JF
4773/* Browser Implementation {{{ */
4774@implementation BrowserView
4775
4776- (void) dealloc {
1cedb821
JF
4777 if (challenge_ != nil)
4778 [challenge_ release];
4779
7b0ce2da
JF
4780 WebView *webview = [webview_ webView];
4781 [webview setFrameLoadDelegate:nil];
4782 [webview setResourceLoadDelegate:nil];
4783 [webview setUIDelegate:nil];
c390d3ab
JF
4784 [webview setScriptDebugDelegate:nil];
4785 [webview setPolicyDelegate:nil];
4786
4787 [webview setDownloadDelegate:nil];
4788
4789 [webview _setFormDelegate:nil];
4790 [webview _setUIKitDelegate:nil];
4791 [webview setWebMailDelegate:nil];
4792 [webview setEditingDelegate:nil];
7b0ce2da 4793
7b0ce2da 4794 [webview_ setDelegate:nil];
baf80942 4795 [webview_ setGestureDelegate:nil];
7b0ce2da 4796
c390d3ab
JF
4797 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4798
c390d3ab
JF
4799 [webview close];
4800
affeffc7
JF
4801#if RecycleWebViews
4802 [webview_ removeFromSuperview];
4803 [Documents_ addObject:[webview_ autorelease]];
4804#else
7b0ce2da 4805 [webview_ release];
affeffc7 4806#endif
baf80942
JF
4807
4808 [indirect_ setDelegate:nil];
4809 [indirect_ release];
4810
4811 [scroller_ setDelegate:nil];
4812
4813 [scroller_ release];
7b0ce2da
JF
4814 [urls_ release];
4815 [indicator_ release];
4816 if (title_ != nil)
4817 [title_ release];
4818 [super dealloc];
4819}
4820
4821- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4822 [self loadRequest:[NSURLRequest
4823 requestWithURL:url
4824 cachePolicy:policy
4825 timeoutInterval:30.0
4826 ]];
4827}
4828
4829- (void) loadURL:(NSURL *)url {
4830 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4831}
4832
4833- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4834 NSMutableURLRequest *copy = [request mutableCopy];
4835
c390d3ab
JF
4836 if (Machine_ != NULL)
4837 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4838 if (UniqueID_ != nil)
4839 [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
7b0ce2da
JF
4840
4841 if (Role_ != nil)
4842 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4843
4844 return copy;
4845}
4846
4847- (void) loadRequest:(NSURLRequest *)request {
4848 pushed_ = true;
4849 [webview_ loadRequest:request];
4850}
4851
4852- (void) reloadURL {
87f46a96
JF
4853 if ([urls_ count] == 0)
4854 return;
7b0ce2da
JF
4855 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4856 [urls_ removeLastObject];
4857 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4858}
4859
4860- (WebView *) webView {
4861 return [webview_ webView];
4862}
4863
4864- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4865 [scroller_ setContentSize:frame.size];
4866}
4867
4868- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4869 [self view:sender didSetFrame:frame];
4870}
4871
4872- (void) pushPage:(RVPage *)page {
4873 [self setBackButtonTitle:title_];
4874 [page setDelegate:delegate_];
4875 [book_ pushPage:page];
4876}
4877
affeffc7
JF
4878- (BOOL) getSpecial:(NSURL *)url {
4879 NSString *href([url absoluteString]);
4880 NSString *scheme([[url scheme] lowercaseString]);
4881
7b0ce2da
JF
4882 RVPage *page = nil;
4883
affeffc7 4884 if ([href hasPrefix:@"apptapp://package/"])
c390d3ab 4885 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
770f2a8e 4886 else if ([scheme isEqualToString:@"cydia"]) {
affeffc7 4887 page = [delegate_ pageForURL:url hasTag:NULL];
770f2a8e
JF
4888 if (page == nil)
4889 return false;
4890 } else if (![scheme isEqualToString:@"apptapp"])
a9a0661e 4891 return false;
f641a0e5
JF
4892
4893 if (page != nil)
4894 [self pushPage:page];
a9a0661e 4895 return true;
7b0ce2da
JF
4896}
4897
c390d3ab
JF
4898- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
4899 [window setValue:delegate_ forKey:@"cydia"];
4900}
4901
affeffc7
JF
4902- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
4903 if (NSURL *url = [request URL]) {
4904 if (![self getSpecial:url]) {
4905 NSString *scheme([[url scheme] lowercaseString]);
4906 if ([scheme isEqualToString:@"mailto"])
4907 [delegate_ openMailToURL:url];
4908 else goto use;
4909 }
4910
4911 [listener ignore];
4912 } else use:
4913 [listener use];
4914}
4915
807ae6d7
JF
4916- (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4917 if ([WebView canShowMIMEType:type])
4918 [listener use];
4919 else {
4920 // XXX: handle more mime types!
4921 [listener ignore];
4922 if (frame == [webView mainFrame])
4923 [UIApp openURL:[request URL]];
4924 }
4925}
4926
affeffc7
JF
4927- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
4928 NSURL *url([request URL]);
4929
4930 if (url == nil) use: {
4931 [listener use];
7b0ce2da 4932 return;
affeffc7
JF
4933 }
4934
4935 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
4936
4937 if (
4938 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
4939 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
4940 ) {
4941 open:
4942 [UIApp openURL:url];
4943 ignore:
4944 [listener ignore];
7b0ce2da 4945 return;
affeffc7
JF
4946 }
4947
4948 int store(_not(int));
4949 if (NSURL *itms = [url itmsURL:&store]) {
807ae6d7 4950 NSLog(@"itms#%@#%u#%@", url, store, itms);
affeffc7
JF
4951 if (
4952 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
4953 store == 2 && [capability containsObject:@"com.apple.AppStore"]
4954 ) {
4955 url = itms;
4956 goto open;
4957 }
4958 }
4959
4960 NSString *scheme([[url scheme] lowercaseString]);
4961
4962 if ([scheme isEqualToString:@"tel"]) {
4963 // XXX: intelligence
4964 goto open;
4965 }
4966
4967 if ([scheme isEqualToString:@"mailto"]) {
4968 [delegate_ openMailToURL:url];
4969 goto ignore;
4970 }
4971
4972 if ([self getSpecial:url])
4973 goto ignore;
4974 else if ([WebView _canHandleRequest:request])
4975 goto use;
4976 else if ([url isSpringboardHandledURL])
4977 goto open;
4978 else
4979 goto use;
7b0ce2da
JF
4980}
4981
c390d3ab
JF
4982- (void) webView:(WebView *)sender setStatusText:(NSString *)text {
4983 //lprintf("Status:%s\n", [text UTF8String]);
4984}
4985
4986- (void) _pushPage {
4987 if (pushed_)
4988 return;
4989 pushed_ = true;
4990 [book_ pushPage:self];
4991}
4992
1cedb821
JF
4993- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
4994 NSString *context([sheet context]);
4995
4996 if ([context isEqualToString:@"challenge"]) {
4997 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
4998
4999 switch (button) {
5000 case 1: {
5001 NSString *username([[sheet textFieldAtIndex:0] text]);
5002 NSString *password([[sheet textFieldAtIndex:1] text]);
5003
5004 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5005
5006 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5007 } break;
5008
5009 case 2:
5010 [sender cancelAuthenticationChallenge:challenge_];
5011 break;
5012
5013 default:
5014 _assert(false);
5015 }
5016
5017 [challenge_ release];
5018 challenge_ = nil;
5019
5020 [sheet dismiss];
5021 }
5022}
5023
5024- (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5025 challenge_ = [challenge retain];
5026
5027 NSURLProtectionSpace *space([challenge protectionSpace]);
5028 NSString *realm([space realm]);
5029 if (realm == nil)
5030 realm = @"";
5031
5032 UIActionSheet *sheet = [[[UIActionSheet alloc]
5033 initWithTitle:realm
5034 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5035 defaultButtonIndex:0
5036 delegate:self
5037 context:@"challenge"
5038 ] autorelease];
5039
5040 [sheet setNumberOfRows:1];
5041
5042 [sheet addTextFieldWithValue:@"" label:@"username"];
5043 [sheet addTextFieldWithValue:@"" label:@"password"];
5044
5045 UITextField *username([sheet textFieldAtIndex:0]); {
5046 UITextInputTraits *traits([username textInputTraits]);
5047 [traits setAutocapitalizationType:0];
5048 [traits setAutocorrectionType:1];
5049 }
5050
5051 UITextField *password([sheet textFieldAtIndex:1]); {
5052 UITextInputTraits *traits([password textInputTraits]);
5053 [traits setAutocapitalizationType:0];
5054 [traits setAutocorrectionType:1];
5055 }
5056
5057 [sheet popupAlertAnimated:YES];
5058}
5059
5060- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
f641a0e5 5061 NSURL *url = [request URL];
affeffc7 5062 if ([self getSpecial:url])
7b0ce2da 5063 return nil;
c390d3ab 5064 [self _pushPage];
7b0ce2da
JF
5065 return [self _addHeadersToRequest:request];
5066}
5067
c390d3ab 5068- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
7b0ce2da
JF
5069 [self setBackButtonTitle:title_];
5070
770f2a8e 5071 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
7b0ce2da
JF
5072 [browser setDelegate:delegate_];
5073
c390d3ab 5074 if (pushed) {
7b0ce2da
JF
5075 [browser loadRequest:[self _addHeadersToRequest:request]];
5076 [book_ pushPage:browser];
5077 }
5078
5079 return [browser webView];
5080}
5081
c390d3ab
JF
5082- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5083 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5084}
5085
5086- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5087 return [self _createWebViewWithRequest:request pushed:YES];
5088}
5089
7b0ce2da 5090- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
25a2158d
JF
5091 if ([frame parentFrame] != nil)
5092 return;
5093
7b0ce2da 5094 title_ = [title retain];
affeffc7 5095 [book_ reloadTitleForPage:self];
7b0ce2da 5096}
36bb2ca2
JF
5097
5098- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5099 if ([frame parentFrame] != nil)
5100 return;
5101
1d3b229d 5102 reloading_ = false;
36bb2ca2
JF
5103 loading_ = true;
5104 [indicator_ startAnimation];
5105 [self reloadButtons];
5106
5107 if (title_ != nil) {
5108 [title_ release];
5109 title_ = nil;
b4d89997 5110 }
36bb2ca2 5111
affeffc7 5112 [book_ reloadTitleForPage:self];
36bb2ca2
JF
5113
5114 WebView *webview = [webview_ webView];
5115 NSString *href = [webview mainFrameURL];
5116 [urls_ addObject:[NSURL URLWithString:href]];
5117
c390d3ab
JF
5118 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5119
7d2ac47f 5120 CGRect webrect = [scroller_ bounds];
36bb2ca2
JF
5121 webrect.size.height = 0;
5122 [webview_ setFrame:webrect];
dc5812ec
JF
5123}
5124
36bb2ca2 5125- (void) _finishLoading {
1d3b229d
JF
5126 if (!reloading_) {
5127 loading_ = false;
5128 [indicator_ stopAnimation];
5129 [self reloadButtons];
5130 }
36bb2ca2 5131}
b4d89997 5132
7d2ac47f 5133- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
7d2ac47f
JF
5134 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5135}
5136
5137- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5138 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5139}
5140
5141- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5142 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5143}
5144
5145- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5146 return [webview_ webView:sender didCommitLoadForFrame:frame];
5147}
5148
5149- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5150 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5151}
5152
36bb2ca2 5153- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
7d2ac47f
JF
5154 if ([frame parentFrame] == nil)
5155 [self _finishLoading];
5156 return [webview_ webView:sender didFinishLoadForFrame:frame];
36bb2ca2 5157}
b4d89997 5158
36bb2ca2
JF
5159- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5160 if ([frame parentFrame] != nil)
5161 return;
36bb2ca2 5162 [self _finishLoading];
7b0ce2da 5163
6d9712c4 5164 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
7b0ce2da 5165 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
6d9712c4
JF
5166 [[error localizedDescription] stringByAddingPercentEscapes]
5167 ]]];
36bb2ca2
JF
5168}
5169
c390d3ab
JF
5170- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5171#if ForSaurik
5172 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5173#endif
5174}
5175
770f2a8e 5176- (id) initWithBook:(RVBook *)book {
36bb2ca2 5177 if ((self = [super initWithBook:book]) != nil) {
36bb2ca2
JF
5178 loading_ = false;
5179
5180 struct CGRect bounds = [self bounds];
5181
5182 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
5183 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5184 [self addSubview:pinstripe];
5185
5186 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5187 [self addSubview:scroller_];
5188
5189 [scroller_ setScrollingEnabled:YES];
5190 [scroller_ setAdjustForContentSizeChange:YES];
5191 [scroller_ setClipsSubviews:YES];
5192 [scroller_ setAllowsRubberBanding:YES];
5193 [scroller_ setScrollDecelerationFactor:0.99];
5194 [scroller_ setDelegate:self];
5195
5196 CGRect webrect = [scroller_ bounds];
5197 webrect.size.height = 0;
5198
affeffc7
JF
5199 WebView *webview;
5200
5201#if RecycleWebViews
baf80942
JF
5202 webview_ = [Documents_ lastObject];
5203 if (webview_ != nil) {
5204 webview_ = [webview_ retain];
affeffc7 5205 webview = [webview_ webView];
baf80942
JF
5206 [Documents_ removeLastObject];
5207 [webview_ setFrame:webrect];
5208 } else {
affeffc7
JF
5209#else
5210 if (true) {
5211#endif
baf80942 5212 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
affeffc7 5213 webview = [webview_ webView];
baf80942
JF
5214
5215 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
36bb2ca2 5216
affeffc7
JF
5217 [webview_ setAllowsMessaging:YES];
5218
baf80942 5219 [webview_ setTilingEnabled:YES];
affeffc7
JF
5220 [webview_ setDrawsGrid:NO];
5221 [webview_ setLogsTilingChanges:NO];
baf80942 5222 [webview_ setTileMinificationFilter:kCAFilterNearest];
affeffc7 5223 [webview_ setDetectsPhoneNumbers:NO];
baf80942 5224 [webview_ setAutoresizes:YES];
7d2ac47f 5225
baf80942
JF
5226 [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
5227 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
5228 [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
7d2ac47f 5229
baf80942 5230 [webview_ _setDocumentType:0x4];
7d2ac47f 5231
baf80942
JF
5232 [webview_ setZoomsFocusedFormControl:YES];
5233 [webview_ setContentsPosition:7];
5234 [webview_ setEnabledGestures:0xa];
bf8476c8
JF
5235 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5236 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
affeffc7 5237
baf80942 5238 [webview_ setSmoothsFonts:YES];
c390d3ab 5239
affeffc7
JF
5240 [webview _setUsesLoaderCache:YES];
5241 [webview setGroupName:@"Cydia"];
5242 //[webview _setLayoutInterval:0.5];
baf80942 5243 }
7d2ac47f 5244
36bb2ca2 5245 [webview_ setDelegate:self];
7d2ac47f 5246 [webview_ setGestureDelegate:self];
baf80942 5247 [scroller_ addSubview:webview_];
b4d89997 5248
c390d3ab
JF
5249 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5250
affeffc7 5251 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
baf80942 5252 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
affeffc7 5253 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
36bb2ca2 5254
770f2a8e 5255 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
36bb2ca2
JF
5256 NSString *application = package == nil ? @"Cydia" : [NSString
5257 stringWithFormat:@"Cydia/%@",
5258 [package installed]
affeffc7 5259 ]; [webview setApplicationNameForUserAgent:application];
36bb2ca2 5260
baf80942
JF
5261 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5262
36bb2ca2 5263 [webview setFrameLoadDelegate:self];
baf80942 5264 [webview setResourceLoadDelegate:indirect_];
36bb2ca2 5265 [webview setUIDelegate:self];
c390d3ab
JF
5266 [webview setScriptDebugDelegate:self];
5267 [webview setPolicyDelegate:self];
36bb2ca2
JF
5268
5269 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
baf80942
JF
5270
5271 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5272 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5273 [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
36bb2ca2 5274 } return self;
dc5812ec
JF
5275}
5276
7d2ac47f
JF
5277- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5278 [webview_ redrawScaledDocument];
5279}
5280
36bb2ca2 5281- (void) _rightButtonClicked {
1d3b229d 5282 reloading_ = true;
36bb2ca2
JF
5283 [self reloadURL];
5284}
5285
6f1a15d9
JF
5286- (NSString *) _rightButtonTitle {
5287 return @"Reload";
5288}
5289
36bb2ca2 5290- (NSString *) rightButtonTitle {
6f1a15d9 5291 return loading_ ? @"" : [self _rightButtonTitle];
36bb2ca2
JF
5292}
5293
5294- (NSString *) title {
affeffc7 5295 return title_ == nil ? @"Loading" : title_;
36bb2ca2
JF
5296}
5297
5298- (NSString *) backButtonTitle {
5299 return @"Browser";
dc5812ec
JF
5300}
5301
36bb2ca2 5302- (void) setPageActive:(BOOL)active {
baf80942 5303 if (!active)
36bb2ca2 5304 [indicator_ removeFromSuperview];
baf80942
JF
5305 else
5306 [[book_ navigationBar] addSubview:indicator_];
dc5812ec
JF
5307}
5308
36bb2ca2 5309- (void) resetViewAnimated:(BOOL)animated {
b4d89997 5310}
2367a917 5311
a0376fc1
JF
5312- (void) setPushed:(bool)pushed {
5313 pushed_ = pushed;
5314}
5315
7b0ce2da
JF
5316@end
5317/* }}} */
aa5e5990 5318
807ae6d7 5319/* Cydia Book {{{ */
7b0ce2da
JF
5320@interface CYBook : RVBook <
5321 ProgressDelegate
5322> {
5323 _transient Database *database_;
baf80942 5324 UINavigationBar *overlay_;
affeffc7 5325 UINavigationBar *underlay_;
7b0ce2da
JF
5326 UIProgressIndicator *indicator_;
5327 UITextLabel *prompt_;
5328 UIProgressBar *progress_;
baf80942 5329 UINavigationButton *cancel_;
7b0ce2da 5330 bool updating_;
baf80942
JF
5331 size_t received_;
5332 NSTimeInterval last_;
aa5e5990
JF
5333}
5334
7b0ce2da
JF
5335- (id) initWithFrame:(CGRect)frame database:(Database *)database;
5336- (void) update;
5337- (BOOL) updating;
aa5e5990
JF
5338
5339@end
aa5e5990 5340
807ae6d7 5341@implementation CYBook
dc5812ec 5342
2367a917 5343- (void) dealloc {
807ae6d7
JF
5344 [overlay_ release];
5345 [indicator_ release];
5346 [prompt_ release];
5347 [progress_ release];
5348 [cancel_ release];
2367a917
JF
5349 [super dealloc];
5350}
5351
807ae6d7
JF
5352- (NSString *) getTitleForPage:(RVPage *)page {
5353 return Simplify([super getTitleForPage:page]);
b4d89997 5354}
dc5812ec 5355
807ae6d7
JF
5356- (BOOL) updating {
5357 return updating_;
6d9712c4
JF
5358}
5359
807ae6d7
JF
5360- (void) update {
5361 [UIView beginAnimations:nil context:NULL];
dc5812ec 5362
807ae6d7
JF
5363 CGRect ovrframe = [overlay_ frame];
5364 ovrframe.origin.y = 0;
5365 [overlay_ setFrame:ovrframe];
dc5812ec 5366
807ae6d7
JF
5367 CGRect barframe = [navbar_ frame];
5368 barframe.origin.y += ovrframe.size.height;
5369 [navbar_ setFrame:barframe];
dc5812ec 5370
807ae6d7
JF
5371 CGRect trnframe = [transition_ frame];
5372 trnframe.origin.y += ovrframe.size.height;
5373 trnframe.size.height -= ovrframe.size.height;
5374 [transition_ setFrame:trnframe];
5a09ae08 5375
807ae6d7 5376 [UIView endAnimations];
2367a917 5377
807ae6d7
JF
5378 [indicator_ startAnimation];
5379 [prompt_ setText:@"Updating Database"];
5380 [progress_ setProgress:0];
2367a917 5381
807ae6d7
JF
5382 received_ = 0;
5383 last_ = [NSDate timeIntervalSinceReferenceDate];
5384 updating_ = true;
5385 [overlay_ addSubview:cancel_];
2367a917 5386
807ae6d7
JF
5387 [NSThread
5388 detachNewThreadSelector:@selector(_update)
5389 toTarget:self
5390 withObject:nil
5391 ];
b4d89997 5392}
2367a917 5393
807ae6d7
JF
5394- (void) _update_ {
5395 updating_ = false;
36bb2ca2 5396
807ae6d7 5397 [indicator_ stopAnimation];
2367a917 5398
807ae6d7 5399 [UIView beginAnimations:nil context:NULL];
6d9712c4 5400
807ae6d7
JF
5401 CGRect ovrframe = [overlay_ frame];
5402 ovrframe.origin.y = -ovrframe.size.height;
5403 [overlay_ setFrame:ovrframe];
dc5812ec 5404
807ae6d7
JF
5405 CGRect barframe = [navbar_ frame];
5406 barframe.origin.y -= ovrframe.size.height;
5407 [navbar_ setFrame:barframe];
dc5812ec 5408
807ae6d7
JF
5409 CGRect trnframe = [transition_ frame];
5410 trnframe.origin.y -= ovrframe.size.height;
5411 trnframe.size.height += ovrframe.size.height;
5412 [transition_ setFrame:trnframe];
2367a917 5413
807ae6d7 5414 [UIView commitAnimations];
baf80942 5415
807ae6d7 5416 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
2367a917
JF
5417}
5418
807ae6d7
JF
5419- (id) initWithFrame:(CGRect)frame database:(Database *)database {
5420 if ((self = [super initWithFrame:frame]) != nil) {
5421 database_ = database;
36bb2ca2 5422
807ae6d7
JF
5423 CGRect ovrrect = [navbar_ bounds];
5424 ovrrect.size.height = [UINavigationBar defaultSize].height;
5425 ovrrect.origin.y = -ovrrect.size.height;
6d9712c4 5426
807ae6d7
JF
5427 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5428 [self addSubview:overlay_];
dc5812ec 5429
807ae6d7
JF
5430 ovrrect.origin.y = frame.size.height;
5431 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5432 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5433 [self addSubview:underlay_];
6d9712c4 5434
807ae6d7
JF
5435 [overlay_ setBarStyle:1];
5436 [underlay_ setBarStyle:1];
5437
5438 int barstyle = [overlay_ _barStyle:NO];
5439 bool ugly = barstyle == 0;
5440
5441 UIProgressIndicatorStyle style = ugly ?
5442 UIProgressIndicatorStyleMediumBrown :
5443 UIProgressIndicatorStyleMediumWhite;
5444
5445 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5446 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5447 CGRect indrect = {{indoffset, indoffset}, indsize};
5448
5449 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5450 [indicator_ setStyle:style];
5451 [overlay_ addSubview:indicator_];
5452
5453 CGSize prmsize = {215, indsize.height + 4};
5454
5455 CGRect prmrect = {{
5456 indoffset * 2 + indsize.width,
5457#ifdef __OBJC2__
5458 -1 +
5459#endif
5460 unsigned(ovrrect.size.height - prmsize.height) / 2
5461 }, prmsize};
5462
5463 UIFont *font = [UIFont systemFontOfSize:15];
5464
5465 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5466
5467 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5468 [prompt_ setBackgroundColor:[UIColor clearColor]];
5469 [prompt_ setFont:font];
5470
5471 [overlay_ addSubview:prompt_];
5472
5473 CGSize prgsize = {75, 100};
5474
5475 CGRect prgrect = {{
5476 ovrrect.size.width - prgsize.width - 10,
5477 (ovrrect.size.height - prgsize.height) / 2
5478 } , prgsize};
5479
5480 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5481 [progress_ setStyle:0];
5482 [overlay_ addSubview:progress_];
5483
5484 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5485 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5486
5487 CGRect frame = [cancel_ frame];
5488 frame.size.width = 65;
5489 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5490 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5491 [cancel_ setFrame:frame];
5492
5493 [cancel_ setBarStyle:barstyle];
5494 } return self;
5495}
5496
5497- (void) _onCancel {
5498 updating_ = false;
5499 [cancel_ removeFromSuperview];
5500}
5501
5502- (void) _update { _pooled
5503 Status status;
5504 status.setDelegate(self);
5505
5506 [database_ updateWithStatus:status];
5507
5508 [self
5509 performSelectorOnMainThread:@selector(_update_)
5510 withObject:nil
5511 waitUntilDone:NO
5512 ];
5513}
5514
5515- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5516 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5517}
5518
5519- (void) setProgressTitle:(NSString *)title {
5520 [self
5521 performSelectorOnMainThread:@selector(_setProgressTitle:)
5522 withObject:title
5523 waitUntilDone:YES
5524 ];
5525}
5526
5527- (void) setProgressPercent:(float)percent {
5528 [self
5529 performSelectorOnMainThread:@selector(_setProgressPercent:)
5530 withObject:[NSNumber numberWithFloat:percent]
5531 waitUntilDone:YES
5532 ];
5533}
5534
5535- (void) startProgress {
5536}
5537
5538- (void) addProgressOutput:(NSString *)output {
5539 [self
5540 performSelectorOnMainThread:@selector(_addProgressOutput:)
5541 withObject:output
5542 waitUntilDone:YES
5543 ];
5544}
5545
5546- (bool) isCancelling:(size_t)received {
5547 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5548 if (received_ != received) {
5549 received_ = received;
5550 last_ = now;
5551 } else if (now - last_ > 15)
5552 return true;
5553 return !updating_;
5554}
5555
5556- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5557 [sheet dismiss];
5558}
5559
5560- (void) _setProgressTitle:(NSString *)title {
5561 [prompt_ setText:title];
5562}
5563
5564- (void) _setProgressPercent:(NSNumber *)percent {
5565 [progress_ setProgress:[percent floatValue]];
5566}
5567
5568- (void) _addProgressOutput:(NSString *)output {
5569}
5570
5571@end
5572/* }}} */
5573/* Cydia:// Protocol {{{ */
5574@interface CydiaURLProtocol : NSURLProtocol {
5575}
5576
5577@end
5578
5579@implementation CydiaURLProtocol
5580
5581+ (BOOL) canInitWithRequest:(NSURLRequest *)request {
5582 NSURL *url([request URL]);
5583 if (url == nil)
5584 return NO;
5585 NSString *scheme([[url scheme] lowercaseString]);
5586 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5587 return NO;
5588 return YES;
5589}
5590
5591+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5592 return request;
5593}
5594
5595- (void) startLoading {
5596 id<NSURLProtocolClient> client([self client]);
5597 NSURLRequest *request([self request]);
5598
5599 NSURL *url([request URL]);
5600 NSString *href([url absoluteString]);
5601
5602 NSString *path([href substringFromIndex:8]);
5603 NSRange slash([path rangeOfString:@"/"]);
5604
5605 NSString *command;
5606 if (slash.location == NSNotFound) {
5607 command = path;
5608 path = nil;
5609 } else {
5610 command = [path substringToIndex:slash.location];
5611 path = [path substringFromIndex:(slash.location + 1)];
5612 }
5613
5614 Database *database([Database sharedInstance]);
5615
5616 if ([command isEqualToString:@"package-icon"]) {
5617 if (path == nil)
5618 goto fail;
dec6029f 5619 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
807ae6d7
JF
5620 Package *package([database packageWithName:path]);
5621 if (package == nil)
5622 goto fail;
5623
dec6029f
JF
5624 UIImage *icon([package icon]);
5625
5626 NSData *data(UIImagePNGRepresentation(icon));
5627
807ae6d7 5628 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
dec6029f
JF
5629 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5630 [client URLProtocol:self didLoadData:data];
5631 [client URLProtocolDidFinishLoading:self];
5632 } else if ([command isEqualToString:@"section-icon"]) {
5633 if (path == nil)
5634 goto fail;
5635 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5636 NSString *section(Simplify(path));
5637
5638 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5639 if (icon == nil)
5640 icon = [UIImage applicationImageNamed:@"unknown.png"];
807ae6d7 5641
807ae6d7
JF
5642 NSData *data(UIImagePNGRepresentation(icon));
5643
dec6029f 5644 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
807ae6d7
JF
5645 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5646 [client URLProtocol:self didLoadData:data];
5647 [client URLProtocolDidFinishLoading:self];
5648 } else fail: {
5649 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5650 }
5651}
5652
5653- (void) stopLoading {
5654}
5655
5656@end
5657/* }}} */
5658
5659/* Install View {{{ */
5660@interface InstallView : RVPage {
5661 _transient Database *database_;
5662 NSMutableArray *sections_;
5663 NSMutableArray *filtered_;
5664 UITransitionView *transition_;
5665 UITable *list_;
5666 UIView *accessory_;
5667 BOOL editing_;
5668}
5669
5670- (id) initWithBook:(RVBook *)book database:(Database *)database;
5671- (void) reloadData;
5672- (void) resetView;
5673
5674@end
5675
5676@implementation InstallView
5677
5678- (void) dealloc {
5679 [list_ setDataSource:nil];
5680 [list_ setDelegate:nil];
5681
5682 [sections_ release];
5683 [filtered_ release];
5684 [transition_ release];
5685 [list_ release];
5686 [accessory_ release];
5687 [super dealloc];
5688}
5689
5690- (int) numberOfRowsInTable:(UITable *)table {
5691 return editing_ ? [sections_ count] : [filtered_ count] + 1;
5692}
5693
5694- (float) table:(UITable *)table heightForRow:(int)row {
5695 return 45;
5696}
5697
5698- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5699 if (reusing == nil)
5700 reusing = [[[SectionCell alloc] init] autorelease];
5701 [(SectionCell *)reusing setSection:(editing_ ?
5702 [sections_ objectAtIndex:row] :
5703 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
5704 ) editing:editing_];
5705 return reusing;
5706}
5707
5708- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
5709 return !editing_;
5710}
5711
5712- (BOOL) table:(UITable *)table canSelectRow:(int)row {
5713 return !editing_;
5714}
5715
5716- (void) tableRowSelected:(NSNotification *)notification {
5717 int row = [[notification object] selectedRow];
5718 if (row == INT_MAX)
5719 return;
5720
5721 Section *section;
5722 NSString *name;
5723 NSString *title;
5724
5725 if (row == 0) {
5726 section = nil;
5727 name = nil;
5728 title = @"All Packages";
5729 } else {
5730 section = [filtered_ objectAtIndex:(row - 1)];
5731 name = [section name];
5732
5733 if (name != nil)
5734 title = name;
5735 else {
5736 name = @"";
5737 title = @"(No Section)";
5738 }
5739 }
5740
5741 PackageTable *table = [[[PackageTable alloc]
5742 initWithBook:book_
5743 database:database_
5744 title:title
5745 filter:@selector(isVisiblyUninstalledInSection:)
5746 with:name
5747 ] autorelease];
5748
5749 [table setDelegate:delegate_];
5750
5751 [book_ pushPage:table];
5752}
5753
5754- (id) initWithBook:(RVBook *)book database:(Database *)database {
5755 if ((self = [super initWithBook:book]) != nil) {
5756 database_ = database;
5757
5758 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
5759 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
5760
5761 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
5762 [self addSubview:transition_];
5763
5764 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
5765 [transition_ transition:0 toView:list_];
5766
5767 UITableColumn *column = [[[UITableColumn alloc]
5768 initWithTitle:@"Name"
5769 identifier:@"name"
5770 width:[self frame].size.width
5771 ] autorelease];
5772
5773 [list_ setDataSource:self];
5774 [list_ setSeparatorStyle:1];
5775 [list_ addTableColumn:column];
5776 [list_ setDelegate:self];
5777 [list_ setReusesTableCells:YES];
5778
5779 [self reloadData];
5780
5781 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5782 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5783 } return self;
5784}
5785
5786- (void) reloadData {
5787 NSArray *packages = [database_ packages];
5788
5789 [sections_ removeAllObjects];
5790 [filtered_ removeAllObjects];
5791
5792 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
5793 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
5794
5795 _trace();
5796 for (size_t i(0); i != [packages count]; ++i) {
5797 Package *package([packages objectAtIndex:i]);
5798 NSString *name([package section]);
5799
5800 if (name != nil) {
5801 Section *section([sections objectForKey:name]);
5802 if (section == nil) {
5803 section = [[[Section alloc] initWithName:name] autorelease];
5804 [sections setObject:section forKey:name];
5805 }
5806 }
6d9712c4
JF
5807
5808 if ([package valid] && [package installed] == nil && [package visible])
5809 [filtered addObject:package];
b4d89997 5810 }
807ae6d7 5811 _trace();
dc5812ec 5812
6d9712c4
JF
5813 [sections_ addObjectsFromArray:[sections allValues]];
5814 [sections_ sortUsingSelector:@selector(compareByName:)];
5815
807ae6d7 5816 _trace();
6d9712c4 5817 [filtered sortUsingSelector:@selector(compareBySection:)];
807ae6d7 5818 _trace();
dc5812ec 5819
b4d89997 5820 Section *section = nil;
6d9712c4
JF
5821 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
5822 Package *package = [filtered objectAtIndex:offset];
b4d89997 5823 NSString *name = [package section];
2367a917 5824
36bb2ca2 5825 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
6d9712c4
JF
5826 section = name == nil ?
5827 [[[Section alloc] initWithName:nil] autorelease] :
5828 [sections objectForKey:name];
5829 [filtered_ addObject:section];
b4d89997 5830 }
dc5812ec 5831
36bb2ca2 5832 [section addToCount];
b4d89997 5833 }
807ae6d7 5834 _trace();
2367a917 5835
b4d89997 5836 [list_ reloadData];
807ae6d7 5837 _trace();
36bb2ca2 5838}
2367a917 5839
6d9712c4
JF
5840- (void) resetView {
5841 if (editing_)
5842 [self _rightButtonClicked];
5843}
5844
36bb2ca2
JF
5845- (void) resetViewAnimated:(BOOL)animated {
5846 [list_ resetViewAnimated:animated];
5847}
2367a917 5848
6d9712c4
JF
5849- (void) _rightButtonClicked {
5850 if ((editing_ = !editing_))
5851 [list_ reloadData];
807ae6d7 5852 else
6d9712c4 5853 [delegate_ updateData];
affeffc7 5854 [book_ reloadTitleForPage:self];
6d9712c4
JF
5855 [book_ reloadButtonsForPage:self];
5856}
5857
36bb2ca2 5858- (NSString *) title {
6d9712c4 5859 return editing_ ? @"Section Visibility" : @"Install by Section";
2367a917
JF
5860}
5861
36bb2ca2
JF
5862- (NSString *) backButtonTitle {
5863 return @"Sections";
dc5812ec
JF
5864}
5865
6d9712c4
JF
5866- (NSString *) rightButtonTitle {
5867 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
5868}
5869
baf80942
JF
5870- (UINavigationButtonStyle) rightButtonStyle {
5871 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
5872}
5873
a54b1c10
JF
5874- (UIView *) accessoryView {
5875 return accessory_;
5876}
5877
dc5812ec 5878@end
2367a917 5879/* }}} */
b4d89997 5880/* Changes View {{{ */
36bb2ca2
JF
5881@interface ChangesView : RVPage {
5882 _transient Database *database_;
dc5812ec
JF
5883 NSMutableArray *packages_;
5884 NSMutableArray *sections_;
36bb2ca2
JF
5885 UISectionList *list_;
5886 unsigned upgrades_;
dc5812ec
JF
5887}
5888
36bb2ca2
JF
5889- (id) initWithBook:(RVBook *)book database:(Database *)database;
5890- (void) reloadData;
dc5812ec 5891
dc5812ec
JF
5892@end
5893
b4d89997
JF
5894@implementation ChangesView
5895
5896- (void) dealloc {
36bb2ca2
JF
5897 [[list_ table] setDelegate:nil];
5898 [list_ setDataSource:nil];
5899
b4d89997
JF
5900 [packages_ release];
5901 [sections_ release];
36bb2ca2 5902 [list_ release];
b4d89997
JF
5903 [super dealloc];
5904}
dc5812ec
JF
5905
5906- (int) numberOfSectionsInSectionList:(UISectionList *)list {
5907 return [sections_ count];
5908}
5909
5910- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
5911 return [[sections_ objectAtIndex:section] name];
5912}
5913
5914- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
5915 return [[sections_ objectAtIndex:section] row];
5916}
5917
5918- (int) numberOfRowsInTable:(UITable *)table {
5919 return [packages_ count];
5920}
5921
5922- (float) table:(UITable *)table heightForRow:(int)row {
9bedffaa 5923 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
dc5812ec
JF
5924}
5925
1d80f6b9
JF
5926- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
5927 if (reusing == nil)
36bb2ca2 5928 reusing = [[[PackageCell alloc] init] autorelease];
1d80f6b9
JF
5929 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
5930 return reusing;
dc5812ec
JF
5931}
5932
5933- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
b4d89997 5934 return NO;
dc5812ec
JF
5935}
5936
b4d89997
JF
5937- (void) tableRowSelected:(NSNotification *)notification {
5938 int row = [[notification object] selectedRow];
36bb2ca2
JF
5939 if (row == INT_MAX)
5940 return;
5941 Package *package = [packages_ objectAtIndex:row];
5942 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
5943 [view setDelegate:delegate_];
5944 [view setPackage:package];
5945 [book_ pushPage:view];
dc5812ec
JF
5946}
5947
9a7b04c5
JF
5948- (void) _leftButtonClicked {
5949 [(CYBook *)book_ update];
5950 [self reloadButtons];
5951}
5952
36bb2ca2
JF
5953- (void) _rightButtonClicked {
5954 [delegate_ distUpgrade];
dc5812ec
JF
5955}
5956
36bb2ca2
JF
5957- (id) initWithBook:(RVBook *)book database:(Database *)database {
5958 if ((self = [super initWithBook:book]) != nil) {
5959 database_ = database;
dc5812ec 5960
b4d89997
JF
5961 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
5962 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 5963
36bb2ca2
JF
5964 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
5965 [self addSubview:list_];
5966
3178d79b 5967 [list_ setShouldHideHeaderInShortLists:NO];
b4d89997
JF
5968 [list_ setDataSource:self];
5969 //[list_ setSectionListStyle:1];
dc5812ec 5970
b4d89997 5971 UITableColumn *column = [[[UITableColumn alloc]
dc5812ec
JF
5972 initWithTitle:@"Name"
5973 identifier:@"name"
36bb2ca2 5974 width:[self frame].size.width
b4d89997 5975 ] autorelease];
dc5812ec
JF
5976
5977 UITable *table = [list_ table];
5978 [table setSeparatorStyle:1];
5979 [table addTableColumn:column];
5980 [table setDelegate:self];
1d80f6b9 5981 [table setReusesTableCells:YES];
dc5812ec 5982
36bb2ca2 5983 [self reloadData];
baf80942
JF
5984
5985 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5986 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
dc5812ec
JF
5987 } return self;
5988}
5989
36bb2ca2
JF
5990- (void) reloadData {
5991 NSArray *packages = [database_ packages];
5992
b4d89997 5993 [packages_ removeAllObjects];
36bb2ca2
JF
5994 [sections_ removeAllObjects];
5995
807ae6d7 5996 _trace();
b4d89997
JF
5997 for (size_t i(0); i != [packages count]; ++i) {
5998 Package *package([packages objectAtIndex:i]);
6d9712c4
JF
5999
6000 if (
6001 [package installed] == nil && [package valid] && [package visible] ||
6002 [package upgradableAndEssential:NO]
6003 )
b4d89997
JF
6004 [packages_ addObject:package];
6005 }
2367a917 6006
807ae6d7 6007 _trace();
9487f027 6008 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
807ae6d7 6009 _trace();
dc5812ec 6010
6d9712c4 6011 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
807ae6d7 6012 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
dc5812ec 6013 Section *section = nil;
807ae6d7 6014 NSDate *last = nil;
b4d89997 6015
36bb2ca2
JF
6016 upgrades_ = 0;
6017 bool unseens = false;
6018
199d0ba5 6019 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
36bb2ca2 6020
807ae6d7 6021 _trace();
dc5812ec
JF
6022 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6023 Package *package = [packages_ objectAtIndex:offset];
dc5812ec 6024
807ae6d7 6025 if (![package upgradableAndEssential:YES]) {
36bb2ca2 6026 unseens = true;
b4d89997 6027 NSDate *seen = [package seen];
dc5812ec 6028
807ae6d7
JF
6029 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6030 last = seen;
723a0072 6031
807ae6d7 6032 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
b4d89997
JF
6033 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6034 [sections_ addObject:section];
807ae6d7 6035 [name release];
b4d89997
JF
6036 }
6037
36bb2ca2 6038 [section addToCount];
807ae6d7
JF
6039 } else if ([package ignored])
6040 [ignored addToCount];
6041 else {
6042 ++upgrades_;
6043 [upgradable addToCount];
b4d89997 6044 }
dc5812ec 6045 }
807ae6d7 6046 _trace();
dc5812ec 6047
36bb2ca2 6048 CFRelease(formatter);
b4d89997 6049
36bb2ca2
JF
6050 if (unseens) {
6051 Section *last = [sections_ lastObject];
6052 size_t count = [last count];
6053 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6054 [sections_ removeLastObject];
6055 }
dc5812ec 6056
807ae6d7
JF
6057 if ([ignored count] != 0)
6058 [sections_ insertObject:ignored atIndex:0];
36bb2ca2
JF
6059 if (upgrades_ != 0)
6060 [sections_ insertObject:upgradable atIndex:0];
c25a610d 6061
36bb2ca2
JF
6062 [list_ reloadData];
6063 [self reloadButtons];
3178d79b
JF
6064}
6065
36bb2ca2
JF
6066- (void) resetViewAnimated:(BOOL)animated {
6067 [list_ resetViewAnimated:animated];
3178d79b
JF
6068}
6069
9a7b04c5
JF
6070- (NSString *) leftButtonTitle {
6071 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6072}
6073
36bb2ca2 6074- (NSString *) rightButtonTitle {
f641a0e5 6075 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
3178d79b
JF
6076}
6077
36bb2ca2
JF
6078- (NSString *) title {
6079 return @"Changes";
8fe19fc1
JF
6080}
6081
3178d79b
JF
6082@end
6083/* }}} */
36bb2ca2
JF
6084/* Search View {{{ */
6085@protocol SearchViewDelegate
6086- (void) showKeyboard:(BOOL)show;
6087@end
b4d89997 6088
a0be02eb 6089@interface SearchView : RVPage {
36bb2ca2
JF
6090 UIView *accessory_;
6091 UISearchField *field_;
a0be02eb
JF
6092 UITransitionView *transition_;
6093 PackageTable *table_;
6094 UIPreferencesTable *advanced_;
9b619239 6095 UIView *dimmed_;
a0be02eb 6096 bool flipped_;
ab398adf 6097 bool reload_;
36bb2ca2 6098}
b4d89997 6099
36bb2ca2
JF
6100- (id) initWithBook:(RVBook *)book database:(Database *)database;
6101- (void) reloadData;
b4d89997 6102
3178d79b
JF
6103@end
6104
36bb2ca2 6105@implementation SearchView
3178d79b 6106
b4d89997 6107- (void) dealloc {
36bb2ca2 6108 [field_ setDelegate:nil];
3178d79b 6109
36bb2ca2
JF
6110 [accessory_ release];
6111 [field_ release];
a0be02eb
JF
6112 [transition_ release];
6113 [table_ release];
6114 [advanced_ release];
9b619239 6115 [dimmed_ release];
36bb2ca2 6116 [super dealloc];
b4d89997
JF
6117}
6118
ab398adf
JF
6119- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6120 return 1;
6121}
6122
6123- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6124 switch (group) {
6125 case 0: return @"Advanced Search (Coming Soon!)";
6126
6127 default: _assert(false);
6128 }
6129}
6130
6131- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6132 switch (group) {
6133 case 0: return 0;
6134
6135 default: _assert(false);
6136 }
6137}
6138
2388b078
JF
6139- (void) _showKeyboard:(BOOL)show {
6140 CGSize keysize = [UIKeyboard defaultSize];
6141 CGRect keydown = [book_ pageBounds];
6142 CGRect keyup = keydown;
6143 keyup.size.height -= keysize.height - ButtonBarHeight_;
6144
6145 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6146
6147 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6148 [animation setSignificantRectFields:8];
6149
6150 if (show) {
6151 [animation setStartFrame:keydown];
6152 [animation setEndFrame:keyup];
6153 } else {
6154 [animation setStartFrame:keyup];
6155 [animation setEndFrame:keydown];
6156 }
6157
6158 UIAnimator *animator = [UIAnimator sharedAnimator];
6159
6160 [animator
6161 addAnimations:[NSArray arrayWithObjects:animation, nil]
6162 withDuration:(KeyboardTime_ - delay)
6163 start:!show
6164 ];
6165
6166 if (show)
6167 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6168
6169 [delegate_ showKeyboard:show];
6170}
6171
36bb2ca2 6172- (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2388b078 6173 [self _showKeyboard:YES];
36bb2ca2 6174}
b4d89997 6175
36bb2ca2 6176- (void) textFieldDidResignFirstResponder:(UITextField *)field {
2388b078 6177 [self _showKeyboard:NO];
36bb2ca2 6178}
b4d89997 6179
36bb2ca2 6180- (void) keyboardInputChanged:(UIFieldEditor *)editor {
ab398adf
JF
6181 if (reload_) {
6182 NSString *text([field_ text]);
6183 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6184 [self reloadData];
6185 reload_ = false;
6186 }
6187}
6188
6189- (void) textFieldClearButtonPressed:(UITextField *)field {
6190 reload_ = true;
6191}
6192
6193- (void) keyboardInputShouldDelete:(id)input {
6194 reload_ = true;
36bb2ca2 6195}
b4d89997 6196
36bb2ca2 6197- (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
ab398adf
JF
6198 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6199 reload_ = true;
36bb2ca2 6200 return YES;
ab398adf
JF
6201 } else {
6202 [field_ resignFirstResponder];
6203 return NO;
6204 }
b4d89997
JF
6205}
6206
36bb2ca2 6207- (id) initWithBook:(RVBook *)book database:(Database *)database {
a0be02eb
JF
6208 if ((self = [super initWithBook:book]) != nil) {
6209 CGRect pageBounds = [book_ pageBounds];
6210
7fea16f2 6211 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
a0be02eb 6212 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
7fea16f2 6213 [self addSubview:pinstripe];*/
a0be02eb 6214
a0be02eb
JF
6215 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6216 [self addSubview:transition_];
6217
6218 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6219
ab398adf
JF
6220 [advanced_ setReusesTableCells:YES];
6221 [advanced_ setDataSource:self];
6222 [advanced_ reloadData];
6223
9b619239
JF
6224 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6225 CGColor dimmed(space_, 0, 0, 0, 0.5);
a9543575 6226 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
9b619239 6227
a0be02eb
JF
6228 table_ = [[PackageTable alloc]
6229 initWithBook:book
6230 database:database
6231 title:nil
7d2ac47f 6232 filter:@selector(isUnfilteredAndSearchedForBy:)
a0be02eb
JF
6233 with:nil
6234 ];
6235
ab398adf 6236 [table_ setShouldHideHeaderInShortLists:NO];
a0be02eb
JF
6237 [transition_ transition:0 toView:table_];
6238
8cc774c5
JF
6239 CGRect cnfrect = {{
6240#ifdef __OBJC2__
6241 6 +
6242#endif
6243 1, 38}, {17, 18}};
b4d89997 6244
36bb2ca2 6245 CGRect area;
6d9712c4 6246 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
baf80942 6247 area.origin.y = 1;
8cc774c5
JF
6248
6249 area.size.width =
6250#ifdef __OBJC2__
6251 8 +
6252#endif
6253 [self bounds].size.width - area.origin.x - 18;
6254
36bb2ca2 6255 area.size.height = [UISearchField defaultHeight];
3178d79b 6256
36bb2ca2 6257 field_ = [[UISearchField alloc] initWithFrame:area];
b4d89997 6258
a9543575 6259 UIFont *font = [UIFont systemFontOfSize:16];
36bb2ca2 6260 [field_ setFont:font];
b4d89997 6261
36bb2ca2 6262 [field_ setPlaceholder:@"Package Names & Descriptions"];
36bb2ca2 6263 [field_ setDelegate:self];
b4d89997 6264
87f46a96 6265 [field_ setPaddingTop:5];
8cc774c5 6266
a9543575
JF
6267 UITextInputTraits *traits = [field_ textInputTraits];
6268 [traits setAutocapitalizationType:0];
6269 [traits setAutocorrectionType:1];
6d9712c4
JF
6270 [traits setReturnKeyType:6];
6271
baf80942 6272 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
8cc774c5
JF
6273
6274 accessory_ = [[UIView alloc] initWithFrame:accrect];
795d26fc
JF
6275 [accessory_ addSubview:field_];
6276
6d9712c4 6277 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
36bb2ca2 6278 [configure setShowPressFeedback:YES];
559dec34 6279 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
36bb2ca2 6280 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6d9712c4 6281 [accessory_ addSubview:configure];*/
baf80942
JF
6282
6283 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6284 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
36bb2ca2 6285 } return self;
3178d79b
JF
6286}
6287
a0be02eb 6288- (void) flipPage {
199d0ba5 6289#ifndef __OBJC2__
a0be02eb
JF
6290 LKAnimation *animation = [LKTransition animation];
6291 [animation setType:@"oglFlip"];
6292 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6293 [animation setFillMode:@"extended"];
6294 [animation setTransitionFlags:3];
6295 [animation setDuration:10];
6296 [animation setSpeed:0.35];
7fea16f2
JF
6297 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6298 [[transition_ _layer] addAnimation:animation forKey:0];
a0be02eb
JF
6299 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6300 flipped_ = !flipped_;
199d0ba5 6301#endif
a0be02eb
JF
6302}
6303
36bb2ca2 6304- (void) configurePushed {
a0be02eb
JF
6305 [field_ resignFirstResponder];
6306 [self flipPage];
6307}
6308
807ae6d7
JF
6309- (void) resetViewAnimated:(BOOL)animated {
6310 if (flipped_)
6311 [self flipPage];
6312 [table_ resetViewAnimated:animated];
6313}
3178d79b 6314
807ae6d7
JF
6315- (void) _reloadData {
6316}
b4d89997 6317
807ae6d7
JF
6318- (void) reloadData {
6319 if (flipped_)
6320 [self flipPage];
6321 [table_ setObject:[field_ text]];
6322 [table_ reloadData];
6323 [table_ resetCursor];
6324}
affeffc7 6325
807ae6d7
JF
6326- (UIView *) accessoryView {
6327 return accessory_;
6328}
affeffc7 6329
807ae6d7
JF
6330- (NSString *) title {
6331 return nil;
6332}
dddbc481 6333
807ae6d7
JF
6334- (NSString *) backButtonTitle {
6335 return @"Search";
6336}
795d26fc 6337
807ae6d7
JF
6338- (void) setDelegate:(id)delegate {
6339 [table_ setDelegate:delegate];
6340 [super setDelegate:delegate];
6341}
8fe19fc1 6342
807ae6d7
JF
6343@end
6344/* }}} */
b4d89997 6345
807ae6d7
JF
6346@interface SettingsView : RVPage {
6347 _transient Database *database_;
6348 NSString *name_;
6349 Package *package_;
6350 UIPreferencesTable *table_;
6351 _UISwitchSlider *subscribedSwitch_;
6352 _UISwitchSlider *ignoredSwitch_;
6353 UIPreferencesControlTableCell *subscribedCell_;
6354 UIPreferencesControlTableCell *ignoredCell_;
6355}
b4d89997 6356
807ae6d7 6357- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
b4d89997 6358
807ae6d7 6359@end
b4d89997 6360
807ae6d7 6361@implementation SettingsView
b4d89997 6362
807ae6d7
JF
6363- (void) dealloc {
6364 [table_ setDataSource:nil];
b4d89997 6365
807ae6d7
JF
6366 [name_ release];
6367 if (package_ != nil)
6368 [package_ release];
6369 [table_ release];
6370 [subscribedSwitch_ release];
6371 [ignoredSwitch_ release];
6372 [subscribedCell_ release];
6373 [ignoredCell_ release];
6374 [super dealloc];
6375}
b4d89997 6376
807ae6d7
JF
6377- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6378 if (package_ == nil)
6379 return 0;
b4d89997 6380
807ae6d7
JF
6381 return 2;
6382}
36bb2ca2 6383
807ae6d7
JF
6384- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6385 if (package_ == nil)
6386 return nil;
baf80942 6387
807ae6d7
JF
6388 switch (group) {
6389 case 0: return nil;
6390 case 1: return nil;
baf80942 6391
807ae6d7
JF
6392 default: _assert(false);
6393 }
baf80942 6394
807ae6d7 6395 return nil;
3178d79b
JF
6396}
6397
807ae6d7
JF
6398- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6399 if (package_ == nil)
6400 return NO;
baf80942 6401
807ae6d7
JF
6402 switch (group) {
6403 case 0: return NO;
6404 case 1: return YES;
b4d89997 6405
807ae6d7
JF
6406 default: _assert(false);
6407 }
3178d79b 6408
807ae6d7 6409 return NO;
dc5812ec
JF
6410}
6411
807ae6d7
JF
6412- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6413 if (package_ == nil)
6414 return 0;
dc5812ec 6415
807ae6d7
JF
6416 switch (group) {
6417 case 0: return 1;
6418 case 1: return 1;
3178d79b 6419
807ae6d7
JF
6420 default: _assert(false);
6421 }
baf80942 6422
807ae6d7 6423 return 0;
36bb2ca2 6424}
b4d89997 6425
807ae6d7
JF
6426- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6427 if (package_ == nil)
6428 return;
b4d89997 6429
807ae6d7 6430 _UISwitchSlider *slider([cell control]);
807ae6d7
JF
6431 BOOL value([slider value] != 0);
6432 NSMutableDictionary *metadata([package_ metadata]);
baf80942 6433
807ae6d7
JF
6434 BOOL before;
6435 if (NSNumber *number = [metadata objectForKey:key])
6436 before = [number boolValue];
6437 else
6438 before = NO;
36bb2ca2 6439
807ae6d7
JF
6440 if (value != before) {
6441 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6442 Changed_ = true;
807ae6d7
JF
6443 [delegate_ updateData];
6444 }
baf80942
JF
6445}
6446
807ae6d7
JF
6447- (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6448 [self onSomething:cell withKey:@"IsSubscribed"];
36bb2ca2 6449}
b4d89997 6450
807ae6d7
JF
6451- (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6452 [self onSomething:cell withKey:@"IsIgnored"];
8fe19fc1
JF
6453}
6454
807ae6d7
JF
6455- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6456 if (package_ == nil)
6457 return nil;
770f2a8e 6458
807ae6d7
JF
6459 switch (group) {
6460 case 0: switch (row) {
6461 case 0:
6462 return subscribedCell_;
6463 case 1:
6464 return ignoredCell_;
6465 default: _assert(false);
6466 } break;
6467
6468 case 1: switch (row) {
6469 case 0: {
6470 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
9487f027
JF
6471 [cell setShowSelection:NO];
6472 [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
6473 return cell;
6474 }
770f2a8e 6475
807ae6d7
JF
6476 default: _assert(false);
6477 } break;
770f2a8e 6478
807ae6d7
JF
6479 default: _assert(false);
6480 }
770f2a8e 6481
807ae6d7 6482 return nil;
770f2a8e
JF
6483}
6484
807ae6d7
JF
6485- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6486 if ((self = [super initWithBook:book])) {
6487 database_ = database;
6488 name_ = [package retain];
770f2a8e 6489
807ae6d7
JF
6490 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6491 [self addSubview:table_];
770f2a8e 6492
807ae6d7
JF
6493 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6494 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
770f2a8e 6495
807ae6d7
JF
6496 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6497 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
770f2a8e 6498
807ae6d7 6499 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
9487f027 6500 [subscribedCell_ setShowSelection:NO];
807ae6d7
JF
6501 [subscribedCell_ setTitle:@"Show All Changes"];
6502 [subscribedCell_ setControl:subscribedSwitch_];
770f2a8e 6503
807ae6d7 6504 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
9487f027 6505 [ignoredCell_ setShowSelection:NO];
807ae6d7
JF
6506 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6507 [ignoredCell_ setControl:ignoredSwitch_];
770f2a8e 6508
807ae6d7
JF
6509 [table_ setDataSource:self];
6510 [self reloadData];
6511 } return self;
6512}
770f2a8e 6513
807ae6d7
JF
6514- (void) resetViewAnimated:(BOOL)animated {
6515 [table_ resetViewAnimated:animated];
6516}
770f2a8e 6517
807ae6d7
JF
6518- (void) reloadData {
6519 if (package_ != nil)
6520 [package_ autorelease];
6521 package_ = [database_ packageWithName:name_];
6522 if (package_ != nil) {
6523 [package_ retain];
6524 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6525 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
770f2a8e 6526 }
807ae6d7
JF
6527
6528 [table_ reloadData];
770f2a8e
JF
6529}
6530
807ae6d7
JF
6531- (NSString *) title {
6532 return @"Settings";
770f2a8e
JF
6533}
6534
6535@end
6536
807ae6d7 6537/* Signature View {{{ */
770f2a8e
JF
6538@interface SignatureView : BrowserView {
6539 _transient Database *database_;
6540 NSString *package_;
6541}
6542
6543- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6544
6545@end
6546
6547@implementation SignatureView
6548
6549- (void) dealloc {
6550 [package_ release];
6551 [super dealloc];
6552}
6553
6554- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6555 // XXX: dude!
6556 [super webView:sender didClearWindowObject:window forFrame:frame];
6557}
6558
6559- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6560 if ((self = [super initWithBook:book]) != nil) {
6561 database_ = database;
6562 package_ = [package retain];
6563 [self reloadData];
6564 } return self;
6565}
6566
807ae6d7
JF
6567- (void) resetViewAnimated:(BOOL)animated {
6568}
6569
770f2a8e
JF
6570- (void) reloadData {
6571 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6572}
6573
6574@end
807ae6d7 6575/* }}} */
770f2a8e 6576
2367a917 6577@interface Cydia : UIApplication <
3178d79b 6578 ConfirmationViewDelegate,
b4d89997 6579 ProgressViewDelegate,
5f6bff8c
JF
6580 SearchViewDelegate,
6581 CydiaDelegate
2367a917 6582> {
dc5812ec 6583 UIWindow *window_;
36bb2ca2 6584
3178d79b
JF
6585 UIView *underlay_;
6586 UIView *overlay_;
36bb2ca2 6587 CYBook *book_;
a9543575 6588 UIToolbar *buttonbar_;
3178d79b 6589
affeffc7 6590 RVBook *confirm_;
9bedffaa 6591
a54b1c10 6592 NSMutableArray *essential_;
9bedffaa 6593 NSMutableArray *broken_;
dc5812ec
JF
6594
6595 Database *database_;
6596 ProgressView *progress_;
6597
06aa974d 6598 unsigned tag_;
b4d89997
JF
6599
6600 UIKeyboard *keyboard_;
bd150f54 6601 UIProgressHUD *hud_;
9b619239
JF
6602
6603 InstallView *install_;
6604 ChangesView *changes_;
6605 ManageView *manage_;
6606 SearchView *search_;
dc5812ec
JF
6607}
6608
dc5812ec
JF
6609@end
6610
6611@implementation Cydia
6612
9bedffaa
JF
6613- (void) _loaded {
6614 if ([broken_ count] != 0) {
6615 int count = [broken_ count];
6616
a9543575 6617 UIActionSheet *sheet = [[[UIActionSheet alloc]
9bedffaa
JF
6618 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6619 buttons:[NSArray arrayWithObjects:
6620 @"Forcibly Clear",
6621 @"Ignore (Temporary)",
6622 nil]
6623 defaultButtonIndex:0
6624 delegate:self
6625 context:@"fixhalf"
6626 ] autorelease];
6627
31f3cfff 6628 [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
6629 [sheet popupAlertAnimated:YES];
6630 } else if (!Ignored_ && [essential_ count] != 0) {
6631 int count = [essential_ count];
6632
a9543575 6633 UIActionSheet *sheet = [[[UIActionSheet alloc]
9bedffaa 6634 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
affeffc7
JF
6635 buttons:[NSArray arrayWithObjects:
6636 @"Upgrade Essential",
9487f027 6637 @"Complete Upgrade",
affeffc7
JF
6638 @"Ignore (Temporary)",
6639 nil]
9bedffaa
JF
6640 defaultButtonIndex:0
6641 delegate:self
6642 context:@"upgrade"
6643 ] autorelease];
6644
31f3cfff 6645 [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
6646 [sheet popupAlertAnimated:YES];
6647 }
6648}
6649
36bb2ca2
JF
6650- (void) _reloadData {
6651 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6652 [hud setText:@"Reloading Data"];
6653 [overlay_ addSubview:hud];
6654 [hud show:YES];*/
dc5812ec 6655
dc5812ec 6656 [database_ reloadData];
c25a610d 6657
36bb2ca2 6658 size_t changes(0);
9bedffaa 6659
a54b1c10 6660 [essential_ removeAllObjects];
9bedffaa 6661 [broken_ removeAllObjects];
b4d89997 6662
36bb2ca2 6663 NSArray *packages = [database_ packages];
affeffc7 6664 for (Package *package in packages) {
9bedffaa
JF
6665 if ([package half])
6666 [broken_ addObject:package];
31f3cfff 6667 if ([package upgradableAndEssential:NO]) {
a54b1c10
JF
6668 if ([package essential])
6669 [essential_ addObject:package];
36bb2ca2 6670 ++changes;
a54b1c10 6671 }
36bb2ca2 6672 }
b4d89997 6673
36bb2ca2
JF
6674 if (changes != 0) {
6675 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
c25a610d 6676 [buttonbar_ setBadgeValue:badge forButton:3];
9e98e020
JF
6677 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6678 [buttonbar_ setBadgeAnimated:YES forButton:3];
c25a610d
JF
6679 [self setApplicationBadge:badge];
6680 } else {
6681 [buttonbar_ setBadgeValue:nil forButton:3];
9e98e020
JF
6682 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
6683 [buttonbar_ setBadgeAnimated:NO forButton:3];
c25a610d
JF
6684 [self removeApplicationBadge];
6685 }
b4d89997 6686
6d9712c4
JF
6687 [self updateData];
6688
c390d3ab 6689#if !ForSaurik
6f1a15d9
JF
6690 if ([packages count] == 0);
6691 else if (Loaded_)
c390d3ab 6692#endif
6d9712c4 6693 [self _loaded];
c390d3ab 6694#if !ForSaurik
6f1a15d9 6695 else {
6d9712c4
JF
6696 Loaded_ = YES;
6697 [book_ update];
6f1a15d9 6698 }
c390d3ab 6699#endif
6d9712c4
JF
6700
6701 /*[hud show:NO];
6702 [hud removeFromSuperview];*/
6703}
6704
7b0ce2da 6705- (void) _saveConfig {
bbb879fb 6706 if (Changed_) {
807ae6d7 6707 _trace();
bbb879fb 6708 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
807ae6d7 6709 _trace();
bbb879fb
JF
6710 Changed_ = false;
6711 }
7b0ce2da
JF
6712}
6713
6714- (void) updateData {
6715 [self _saveConfig];
36bb2ca2 6716
9b619239 6717 /* XXX: this is just stupid */
807ae6d7 6718 if (tag_ != 2 && install_ != nil)
9b619239 6719 [install_ reloadData];
807ae6d7 6720 if (tag_ != 3 && changes_ != nil)
9b619239 6721 [changes_ reloadData];
807ae6d7 6722 if (tag_ != 5 && search_ != nil)
9b619239
JF
6723 [search_ reloadData];
6724
36bb2ca2 6725 [book_ reloadData];
b4d89997
JF
6726}
6727
7b0ce2da
JF
6728- (void) update_ {
6729 [database_ update];
6730}
6731
6732- (void) syncData {
6733 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
6734 _assert(file != NULL);
6735
6736 NSArray *keys = [Sources_ allKeys];
6737
6738 for (int i(0), e([keys count]); i != e; ++i) {
6739 NSString *key = [keys objectAtIndex:i];
6740 NSDictionary *source = [Sources_ objectForKey:key];
6741
6742 fprintf(file, "%s %s %s\n",
6743 [[source objectForKey:@"Type"] UTF8String],
6744 [[source objectForKey:@"URI"] UTF8String],
6745 [[source objectForKey:@"Distribution"] UTF8String]
6746 );
6747 }
6748
6749 fclose(file);
6750
6751 [self _saveConfig];
6752
6753 [progress_
6754 detachNewThreadSelector:@selector(update_)
6755 toTarget:self
6756 withObject:nil
baf80942 6757 title:@"Updating Sources"
7b0ce2da
JF
6758 ];
6759}
6760
36bb2ca2
JF
6761- (void) reloadData {
6762 @synchronized (self) {
6763 if (confirm_ == nil)
6764 [self _reloadData];
6765 }
b4d89997
JF
6766}
6767
6768- (void) resolve {
6769 pkgProblemResolver *resolver = [database_ resolver];
6770
6771 resolver->InstallProtect();
6772 if (!resolver->Resolve(true))
6773 _error->Discard();
2367a917
JF
6774}
6775
6776- (void) perform {
8fe19fc1 6777 [database_ prepare];
49048579 6778
affeffc7
JF
6779 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
6780 [confirm_ setDelegate:self];
49048579 6781
affeffc7
JF
6782 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
6783 [page setDelegate:self];
49048579 6784
affeffc7
JF
6785 [confirm_ setPage:page];
6786 [underlay_ popSubview:confirm_];
3178d79b
JF
6787}
6788
36bb2ca2
JF
6789- (void) installPackage:(Package *)package {
6790 @synchronized (self) {
6791 [package install];
6792 [self resolve];
6793 [self perform];
6794 }
6795}
6796
6797- (void) removePackage:(Package *)package {
6798 @synchronized (self) {
6799 [package remove];
6800 [self resolve];
6801 [self perform];
6802 }
6803}
6804
6805- (void) distUpgrade {
6806 @synchronized (self) {
6807 [database_ upgrade];
6808 [self perform];
6809 }
b4d89997
JF
6810}
6811
3178d79b 6812- (void) cancel {
36bb2ca2 6813 @synchronized (self) {
36bb2ca2 6814 [self _reloadData];
c390d3ab
JF
6815 if (confirm_ != nil) {
6816 [confirm_ release];
6817 confirm_ = nil;
6818 }
36bb2ca2 6819 }
3178d79b
JF
6820}
6821
6822- (void) confirm {
6823 [overlay_ removeFromSuperview];
77fcccaf 6824 reload_ = true;
3178d79b 6825
2367a917
JF
6826 [progress_
6827 detachNewThreadSelector:@selector(perform)
6828 toTarget:database_
6829 withObject:nil
baf80942 6830 title:@"Running"
2367a917
JF
6831 ];
6832}
6833
b31b87cc
JF
6834- (void) bootstrap_ {
6835 [database_ update];
6836 [database_ upgrade];
6837 [database_ prepare];
6838 [database_ perform];
6839}
6840
6841- (void) bootstrap {
6842 [progress_
6843 detachNewThreadSelector:@selector(bootstrap_)
6844 toTarget:self
6845 withObject:nil
baf80942 6846 title:@"Bootstrap Install"
b31b87cc
JF
6847 ];
6848}
6849
2367a917 6850- (void) progressViewIsComplete:(ProgressView *)progress {
c390d3ab
JF
6851 if (confirm_ != nil) {
6852 [underlay_ addSubview:overlay_];
affeffc7 6853 [confirm_ popFromSuperviewAnimated:NO];
3178d79b 6854 }
c390d3ab
JF
6855
6856 [self cancel];
dc5812ec
JF
6857}
6858
36bb2ca2 6859- (void) setPage:(RVPage *)page {
9b619239 6860 [page resetViewAnimated:NO];
36bb2ca2
JF
6861 [page setDelegate:self];
6862 [book_ setPage:page];
dc5812ec
JF
6863}
6864
7b0ce2da 6865- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
770f2a8e 6866 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
7b0ce2da 6867 [browser loadURL:url];
36bb2ca2 6868 return browser;
2367a917
JF
6869}
6870
7b0ce2da
JF
6871- (void) _setHomePage {
6872 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
6873}
6874
dc5812ec 6875- (void) buttonBarItemTapped:(id)sender {
06aa974d 6876 unsigned tag = [sender tag];
559dec34
JF
6877 if (tag == tag_) {
6878 [book_ resetViewAnimated:YES];
6879 return;
6d9712c4
JF
6880 } else if (tag_ == 2 && tag != 2)
6881 [install_ resetView];
dc5812ec 6882
06aa974d 6883 switch (tag) {
11108eb6 6884 case 1: [self _setHomePage]; break;
36bb2ca2 6885
11108eb6
JF
6886 case 2: [self setPage:install_]; break;
6887 case 3: [self setPage:changes_]; break;
6888 case 4: [self setPage:manage_]; break;
6889 case 5: [self setPage:search_]; break;
36bb2ca2 6890
11108eb6 6891 default: _assert(false);
dc5812ec
JF
6892 }
6893
06aa974d 6894 tag_ = tag;
06aa974d
JF
6895}
6896
b4d89997 6897- (void) applicationWillSuspend {
77fcccaf 6898 [database_ clean];
eeb9b112 6899 [super applicationWillSuspend];
b4d89997
JF
6900}
6901
7b0ce2da 6902- (void) askForSettings {
a9543575 6903 UIActionSheet *role = [[[UIActionSheet alloc]
7b0ce2da
JF
6904 initWithTitle:@"Who Are You?"
6905 buttons:[NSArray arrayWithObjects:
6906 @"User (Graphical Only)",
f641a0e5 6907 @"Hacker (+ Command Line)",
7b0ce2da
JF
6908 @"Developer (No Filters)",
6909 nil]
6910 defaultButtonIndex:-1
6911 delegate:self
6912 context:@"role"
6913 ] autorelease];
6914
6915 [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."];
6916 [role popupAlertAnimated:YES];
6917}
6918
bd150f54
JF
6919- (void) finish {
6920 if (hud_ != nil) {
6921 [self setStatusBarShowsProgress:NO];
a54b1c10 6922
bd150f54
JF
6923 [hud_ show:NO];
6924 [hud_ removeFromSuperview];
6925 [hud_ autorelease];
6926 hud_ = nil;
f9d1e9a0 6927
c6ad32a6
JF
6928 pid_t pid = ExecFork();
6929 if (pid == 0) {
6930 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
6931 perror("launchctl stop");
6932 }
6933
f9d1e9a0 6934 return;
bd150f54 6935 }
3178d79b 6936
7b0ce2da
JF
6937 if (Role_ == nil) {
6938 [self askForSettings];
6939 return;
6940 }
6941
3178d79b 6942 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
b31b87cc 6943
bd150f54 6944 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
36bb2ca2
JF
6945 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
6946 0, 0, screenrect.size.width, screenrect.size.height - 48
6947 ) database:database_];
dc5812ec 6948
36bb2ca2 6949 [book_ setDelegate:self];
dc5812ec 6950
36bb2ca2 6951 [overlay_ addSubview:book_];
b19871dd 6952
dc5812ec
JF
6953 NSArray *buttonitems = [NSArray arrayWithObjects:
6954 [NSDictionary dictionaryWithObjectsAndKeys:
6955 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
9b619239
JF
6956 @"home-up.png", kUIButtonBarButtonInfo,
6957 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6958 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
6959 self, kUIButtonBarButtonTarget,
9b619239 6960 @"Home", kUIButtonBarButtonTitle,
dc5812ec
JF
6961 @"0", kUIButtonBarButtonType,
6962 nil],
6963
6964 [NSDictionary dictionaryWithObjectsAndKeys:
6965 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
6966 @"install-up.png", kUIButtonBarButtonInfo,
6967 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
6968 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
6969 self, kUIButtonBarButtonTarget,
6d9712c4 6970 @"Sections", kUIButtonBarButtonTitle,
dc5812ec
JF
6971 @"0", kUIButtonBarButtonType,
6972 nil],
6973
6974 [NSDictionary dictionaryWithObjectsAndKeys:
6975 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
6976 @"changes-up.png", kUIButtonBarButtonInfo,
6977 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6978 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
6979 self, kUIButtonBarButtonTarget,
b4d89997 6980 @"Changes", kUIButtonBarButtonTitle,
dc5812ec
JF
6981 @"0", kUIButtonBarButtonType,
6982 nil],
6983
6984 [NSDictionary dictionaryWithObjectsAndKeys:
6985 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
6986 @"manage-up.png", kUIButtonBarButtonInfo,
6987 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6988 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
6989 self, kUIButtonBarButtonTarget,
36bb2ca2 6990 @"Manage", kUIButtonBarButtonTitle,
dc5812ec
JF
6991 @"0", kUIButtonBarButtonType,
6992 nil],
6993
6994 [NSDictionary dictionaryWithObjectsAndKeys:
6995 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
6996 @"search-up.png", kUIButtonBarButtonInfo,
6997 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
6998 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
6999 self, kUIButtonBarButtonTarget,
b4d89997 7000 @"Search", kUIButtonBarButtonTitle,
dc5812ec
JF
7001 @"0", kUIButtonBarButtonType,
7002 nil],
7003 nil];
7004
a9543575 7005 buttonbar_ = [[UIToolbar alloc]
3178d79b 7006 initInView:overlay_
dc5812ec 7007 withFrame:CGRectMake(
2388b078
JF
7008 0, screenrect.size.height - ButtonBarHeight_,
7009 screenrect.size.width, ButtonBarHeight_
dc5812ec
JF
7010 )
7011 withItemList:buttonitems
7012 ];
7013
2367a917
JF
7014 [buttonbar_ setDelegate:self];
7015 [buttonbar_ setBarStyle:1];
7016 [buttonbar_ setButtonBarTrackingMode:2];
dc5812ec
JF
7017
7018 int buttons[5] = {1, 2, 3, 4, 5};
2367a917
JF
7019 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7020 [buttonbar_ showButtonGroup:0 withDuration:0];
dc5812ec
JF
7021
7022 for (int i = 0; i != 5; ++i)
2367a917 7023 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
2388b078 7024 i * 64 + 2, 1, 60, ButtonBarHeight_
dc5812ec
JF
7025 )];
7026
2367a917 7027 [buttonbar_ showSelectionForButton:1];
3178d79b 7028 [overlay_ addSubview:buttonbar_];
dc5812ec 7029
b4d89997 7030 [UIKeyboard initImplementationNow];
b4d89997 7031 CGSize keysize = [UIKeyboard defaultSize];
ab398adf 7032 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
b4d89997 7033 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
3622197e 7034 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
ab398adf 7035 [overlay_ addSubview:keyboard_];
b4d89997 7036
807ae6d7
JF
7037 if (!bootstrap_)
7038 [underlay_ addSubview:overlay_];
7039
7040 [self reloadData];
7041
9b619239
JF
7042 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7043 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
9b619239
JF
7044 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7045
7b0ce2da
JF
7046 manage_ = (ManageView *) [[self
7047 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7048 withClass:[ManageView class]
7049 ] retain];
7050
b31b87cc
JF
7051 if (bootstrap_)
7052 [self bootstrap];
36bb2ca2 7053 else
9b619239 7054 [self _setHomePage];
b4d89997
JF
7055}
7056
a9543575 7057- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821
JF
7058 NSString *context([sheet context]);
7059
7060 if ([context isEqualToString:@"fixhalf"]) {
7b0ce2da
JF
7061 switch (button) {
7062 case 1:
7063 @synchronized (self) {
7064 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7065 Package *broken = [broken_ objectAtIndex:i];
7066 [broken remove];
7067
7068 NSString *id = [broken id];
7069 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7070 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7071 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7072 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7073 }
7074
7075 [self resolve];
7076 [self perform];
7077 }
7078 break;
7079
7080 case 2:
7081 [broken_ removeAllObjects];
7082 [self _loaded];
7083 break;
7084
7085 default:
7086 _assert(false);
7087 }
1cedb821
JF
7088
7089 [sheet dismiss];
7090 } else if ([context isEqualToString:@"role"]) {
7b0ce2da
JF
7091 switch (button) {
7092 case 1: Role_ = @"User"; break;
7093 case 2: Role_ = @"Hacker"; break;
7094 case 3: Role_ = @"Developer"; break;
7095
7096 default:
7097 Role_ = nil;
7098 _assert(false);
7099 }
7100
7101 bool reset = Settings_ != nil;
7102
7103 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7104 Role_, @"Role",
7105 nil];
7106
7107 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7108
7109 Changed_ = true;
7110
7111 if (reset)
7112 [self updateData];
7113 else
7114 [self finish];
1cedb821
JF
7115
7116 [sheet dismiss];
7117 } else if ([context isEqualToString:@"upgrade"]) {
7b0ce2da
JF
7118 switch (button) {
7119 case 1:
7120 @synchronized (self) {
7121 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7122 Package *essential = [essential_ objectAtIndex:i];
7123 [essential install];
7124 }
7125
7126 [self resolve];
7127 [self perform];
7128 }
7129 break;
7130
7131 case 2:
affeffc7
JF
7132 [self distUpgrade];
7133 break;
7134
7135 case 3:
7b0ce2da
JF
7136 Ignored_ = YES;
7137 break;
7138
7139 default:
7140 _assert(false);
7141 }
7142
1cedb821
JF
7143 [sheet dismiss];
7144 }
7b0ce2da
JF
7145}
7146
affeffc7 7147- (void) reorganize { _pooled
e64882de
JF
7148 system("/usr/libexec/cydia/free.sh");
7149 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
bd150f54
JF
7150}
7151
7152- (void) applicationSuspend:(__GSEvent *)event {
7153 if (hud_ == nil && ![progress_ isRunning])
7154 [super applicationSuspend:event];
bd150f54
JF
7155}
7156
6d9712c4
JF
7157- (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7158 if (hud_ == nil)
7159 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7160}
7161
7162- (void) _setSuspended:(BOOL)value {
7163 if (hud_ == nil)
7164 [super _setSuspended:value];
7165}
7166
7b0ce2da
JF
7167- (UIProgressHUD *) addProgressHUD {
7168 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7169 [hud show:YES];
7170 [underlay_ addSubview:hud];
7171 return hud;
7172}
7173
c390d3ab 7174- (void) openMailToURL:(NSURL *)url {
86316a91
JF
7175// XXX: this makes me sad
7176#if 0
c390d3ab 7177 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
86316a91
JF
7178#else
7179 [UIApp openURL:url];
7180#endif
c390d3ab
JF
7181}
7182
7183- (RVPage *) pageForPackage:(NSString *)name {
7184 if (Package *package = [database_ packageWithName:name]) {
7185 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7186 [view setPackage:package];
7187 return view;
7188 } else {
7189 UIActionSheet *sheet = [[[UIActionSheet alloc]
7190 initWithTitle:@"Cannot Locate Package"
7191 buttons:[NSArray arrayWithObjects:@"Close", nil]
7192 defaultButtonIndex:0
7193 delegate:self
7194 context:@"missing"
7195 ] autorelease];
7196
7197 [sheet setBodyText:[NSString stringWithFormat:
7198 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7199 , name]];
7200
7201 [sheet popupAlertAnimated:YES];
7202 return nil;
7203 }
7204}
7205
7206- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7207 NSString *href = [url absoluteString];
7208
7209 if (tag != NULL)
7210 tag = 0;
7211
7212 if ([href isEqualToString:@"cydia://add-source"])
7213 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7214 else if ([href isEqualToString:@"cydia://sources"])
7215 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7216 else if ([href isEqualToString:@"cydia://packages"])
7217 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7218 else if ([href hasPrefix:@"cydia://url/"])
7219 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
affeffc7
JF
7220 else if ([href hasPrefix:@"cydia://launch/"])
7221 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
807ae6d7
JF
7222 else if ([href hasPrefix:@"cydia://package-settings/"])
7223 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
770f2a8e
JF
7224 else if ([href hasPrefix:@"cydia://package-signature/"])
7225 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
c390d3ab
JF
7226 else if ([href hasPrefix:@"cydia://package/"])
7227 return [self pageForPackage:[href substringFromIndex:16]];
7228 else if ([href hasPrefix:@"cydia://files/"]) {
7229 NSString *name = [href substringFromIndex:14];
7230
7231 if (Package *package = [database_ packageWithName:name]) {
7232 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7233 [files setPackage:package];
7234 return files;
7235 }
7236 }
7237
7238 return nil;
7239}
7240
7241- (void) applicationOpenURL:(NSURL *)url {
7242 [super applicationOpenURL:url];
7243 int tag;
7244 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7245 [self setPage:page];
7246 [buttonbar_ showSelectionForButton:tag];
7247 tag_ = tag;
7248 }
7249}
7250
bd150f54 7251- (void) applicationDidFinishLaunching:(id)unused {
f641a0e5
JF
7252 Font12_ = [[UIFont systemFontOfSize:12] retain];
7253 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7254 Font14_ = [[UIFont systemFontOfSize:14] retain];
7255 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7256 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7257
bd150f54
JF
7258 _assert(pkgInitConfig(*_config));
7259 _assert(pkgInitSystem(*_config, _system));
7260
bd150f54
JF
7261 tag_ = 1;
7262
7263 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7264 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7265
770f2a8e
JF
7266 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7267
bd150f54
JF
7268 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7269 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7270
f641a0e5
JF
7271 [window_ orderFront:self];
7272 [window_ makeKey:self];
c390d3ab 7273 [window_ setHidden:NO];
bd150f54 7274
770f2a8e 7275 database_ = [Database sharedInstance];
bd150f54
JF
7276 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7277 [database_ setDelegate:progress_];
7278 [window_ setContentView:progress_];
7279
7280 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7281 [progress_ setContentView:underlay_];
7282
7283 [progress_ resetView];
7284
6d9712c4 7285 if (
bd150f54 7286 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
bd150f54 7287 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6d9712c4
JF
7288 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7289 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7290 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
90515f88
JF
7291 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7292 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
bd150f54 7293 ) {
5afbb4b7
JF
7294 [self setIdleTimerDisabled:YES];
7295
7b0ce2da 7296 hud_ = [self addProgressHUD];
1cedb821 7297 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
bd150f54
JF
7298
7299 [self setStatusBarShowsProgress:YES];
7300
7301 [NSThread
7302 detachNewThreadSelector:@selector(reorganize)
7303 toTarget:self
7304 withObject:nil
7305 ];
6d9712c4 7306 } else
bd150f54
JF
7307 [self finish];
7308}
7309
affeffc7 7310/* Web Scripting {{{ */
c390d3ab
JF
7311+ (NSString *) webScriptNameForSelector:(SEL)selector {
7312 if (selector == @selector(supports:))
7313 return @"supports";
7314 return nil;
7315}
7316
c390d3ab 7317+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
c390d3ab
JF
7318 return selector != @selector(supports:);
7319}
7320
affeffc7
JF
7321- (BOOL) supports:(NSString *)feature {
7322 return [feature isEqualToString:@"window.open"];
7323}
7324/* }}} */
7325
b4d89997 7326- (void) showKeyboard:(BOOL)show {
ab398adf
JF
7327 CGSize keysize = [UIKeyboard defaultSize];
7328 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7329 CGRect keyup = keydown;
7330 keyup.origin.y -= keysize.height;
7331
7332 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7333 [animation setSignificantRectFields:2];
7334
7335 if (show) {
7336 [animation setStartFrame:keydown];
7337 [animation setEndFrame:keyup];
7338 [keyboard_ activate];
7339 } else {
7340 [animation setStartFrame:keyup];
7341 [animation setEndFrame:keydown];
7342 [keyboard_ deactivate];
7343 }
7344
7345 [[UIAnimator sharedAnimator]
7346 addAnimations:[NSArray arrayWithObjects:animation, nil]
2388b078 7347 withDuration:KeyboardTime_
ab398adf
JF
7348 start:YES
7349 ];
dc5812ec
JF
7350}
7351
a9543575 7352- (void) slideUp:(UIActionSheet *)alert {
77fcccaf
JF
7353 if (Advanced_)
7354 [alert presentSheetFromButtonBar:buttonbar_];
7355 else
7356 [alert presentSheetInView:overlay_];
36bb2ca2
JF
7357}
7358
dc5812ec
JF
7359@end
7360
affeffc7 7361void AddPreferences(NSString *plist) { _pooled
b4d89997
JF
7362 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7363 _assert(settings != NULL);
7364 NSMutableArray *items = [settings objectForKey:@"items"];
7365
7366 bool cydia(false);
7367
7368 for (size_t i(0); i != [items count]; ++i) {
7369 NSMutableDictionary *item([items objectAtIndex:i]);
7370 NSString *label = [item objectForKey:@"label"];
7371 if (label != nil && [label isEqualToString:@"Cydia"]) {
7372 cydia = true;
7373 break;
7374 }
7375 }
7376
7377 if (!cydia) {
7378 for (size_t i(0); i != [items count]; ++i) {
7379 NSDictionary *item([items objectAtIndex:i]);
7380 NSString *label = [item objectForKey:@"label"];
7381 if (label != nil && [label isEqualToString:@"General"]) {
7382 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7383 @"CydiaSettings", @"bundle",
7384 @"PSLinkCell", @"cell",
7385 [NSNumber numberWithBool:YES], @"hasIcon",
7386 [NSNumber numberWithBool:YES], @"isController",
7387 @"Cydia", @"label",
7388 nil] atIndex:(i + 1)];
7389
7390 break;
7391 }
7392 }
7393
7394 _assert([settings writeToFile:plist atomically:YES] == YES);
7395 }
b4d89997
JF
7396}
7397
7398/*IMP alloc_;
7399id Alloc_(id self, SEL selector) {
7400 id object = alloc_(self, selector);
c390d3ab 7401 lprintf("[%s]A-%p\n", self->isa->name, object);
b4d89997
JF
7402 return object;
7403}*/
7404
36bb2ca2
JF
7405/*IMP dealloc_;
7406id Dealloc_(id self, SEL selector) {
7407 id object = dealloc_(self, selector);
c390d3ab 7408 lprintf("[%s]D-%p\n", self->isa->name, object);
36bb2ca2
JF
7409 return object;
7410}*/
b4d89997 7411
affeffc7 7412int main(int argc, char *argv[]) { _pooled
de3b1ab4
JF
7413 bool substrate(false);
7414
7415 if (argc != 0) {
7416 char **args(argv);
7417 int arge(1);
7418
7419 for (int argi(1); argi != argc; ++argi)
7420 if (strcmp(argv[argi], "--") == 0) {
7421 arge = argi;
7422 argv[argi] = argv[0];
7423 argv += argi;
7424 argc -= argi;
7425 break;
7426 }
7427
7428 for (int argi(1); argi != arge; ++argi)
7429 if (strcmp(args[argi], "--bootstrap") == 0)
7430 bootstrap_ = true;
7431 else if (strcmp(args[argi], "--substrate") == 0)
7432 substrate = true;
7433 else
7434 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7435 }
b31b87cc 7436
7d2ac47f 7437 App_ = [[NSBundle mainBundle] bundlePath];
d73cede2 7438 Home_ = NSHomeDirectory();
87f46a96 7439 Locale_ = CFLocaleCopyCurrent();
d73cede2
JF
7440
7441 {
7442 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7443 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7444 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7445 Sounds_Keyboard_ = [keyboard boolValue];
7446 }
7447
b4d89997
JF
7448 setuid(0);
7449 setgid(0);
7450
bf8476c8 7451#if 1 /* XXX: this costs 1.4s of startup performance */
aa629131
JF
7452 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7453 _assert(errno == ENOENT);
7454 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7455 _assert(errno == ENOENT);
9487f027 7456#endif
aa5e5990 7457
b4d89997
JF
7458 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7459 alloc_ = alloc->method_imp;
7460 alloc->method_imp = (IMP) &Alloc_;*/
7461
36bb2ca2
JF
7462 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7463 dealloc_ = dealloc->method_imp;
7464 dealloc->method_imp = (IMP) &Dealloc_;*/
7465
3178d79b 7466 size_t size;
c390d3ab
JF
7467
7468 int maxproc;
7469 size = sizeof(maxproc);
7470 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7471 perror("sysctlbyname(\"kern.maxproc\", ?)");
7472 else if (maxproc < 64) {
7473 maxproc = 64;
7474 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7475 perror("sysctlbyname(\"kern.maxproc\", #)");
7476 }
7477
3178d79b
JF
7478 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7479 char *machine = new char[size];
c390d3ab
JF
7480 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7481 perror("sysctlbyname(\"hw.machine\", ?)");
7482 else
7483 Machine_ = machine;
3178d79b 7484
87f46a96 7485 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
3178d79b 7486
99364ec4
JF
7487 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7488 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
b4d89997
JF
7489
7490 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7491 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6d9712c4 7492 else {
7b0ce2da
JF
7493 Settings_ = [Metadata_ objectForKey:@"Settings"];
7494
b4d89997 7495 Packages_ = [Metadata_ objectForKey:@"Packages"];
6d9712c4 7496 Sections_ = [Metadata_ objectForKey:@"Sections"];
7b0ce2da
JF
7497 Sources_ = [Metadata_ objectForKey:@"Sources"];
7498 }
7499
7500 if (Settings_ != nil)
7501 Role_ = [Settings_ objectForKey:@"Role"];
7502
7503 if (Packages_ == nil) {
7504 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7505 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7506 }
7507
7508 if (Sections_ == nil) {
7509 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7510 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7511 }
7512
7513 if (Sources_ == nil) {
7514 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7515 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6d9712c4 7516 }
b4d89997 7517
affeffc7 7518#if RecycleWebViews
baf80942 7519 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
affeffc7 7520#endif
baf80942 7521
de3b1ab4 7522 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
dddbc481
JF
7523 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7524
26c2dd8c
JF
7525 if (access("/User", F_OK) != 0)
7526 system("/usr/libexec/cydia/firmware.sh");
9e98e020 7527
87f46a96
JF
7528 _assert([[NSFileManager defaultManager]
7529 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7530 withIntermediateDirectories:YES
7531 attributes:nil
7532 error:NULL
7533 ]);
7534
36bb2ca2
JF
7535 space_ = CGColorSpaceCreateDeviceRGB();
7536
f641a0e5 7537 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
77fcccaf 7538 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
36bb2ca2 7539 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
baf80942 7540 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
36bb2ca2 7541 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7b0ce2da
JF
7542 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7543
bc8cd583
JF
7544 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7545
46dbfd32 7546 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7b0ce2da 7547
87f46a96
JF
7548 UIApplicationUseLegacyEvents(YES);
7549 UIKeyboardDisableAutomaticAppearance();
7550
a9543575 7551 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
36bb2ca2
JF
7552
7553 CGColorSpaceRelease(space_);
199d0ba5 7554 CFRelease(Locale_);
36bb2ca2 7555
36bb2ca2 7556 return value;
dc5812ec 7557}