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