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