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