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