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