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