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