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