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