]> git.saurik.com Git - cydia.git/blame - MobileCydia.mm
Fix localization of LOADING_DATA (use ELISION+LOADING for 'Loading...').
[cydia.git] / MobileCydia.mm
CommitLineData
e057ec05 1/* Cydia - iPhone UIKit Front-End for Debian APT
daf7f6e2 2 * Copyright (C) 2008-2010 Jay Freeman (saurik)
e057ec05
JF
3*/
4
017b2b71 5/* Modified BSD License {{{ */
e057ec05
JF
6/*
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the
12 * above copyright notice, this list of conditions
13 * and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the
15 * above copyright notice, this list of conditions
16 * and the following disclaimer in the documentation
17 * and/or other materials provided with the
18 * distribution.
19 * 3. The name of the author may not be used to endorse
20 * or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
25 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37*/
017b2b71 38/* }}} */
e057ec05 39
83105e6e
JF
40// XXX: wtf/FastMalloc.h... wtf?
41#define USE_SYSTEM_MALLOC 1
42
a75f53e7 43/* #include Directives {{{ */
1e4922b8
JF
44#include "UICaboodle/UCPlatform.h"
45#include "UICaboodle/UCLocalize.h"
f159ecd4 46
59efd93a
JF
47#include <objc/objc.h>
48#include <objc/runtime.h>
49
b0d03ade 50#include <CoreGraphics/CoreGraphics.h>
b0d03ade 51#include <Foundation/Foundation.h>
853d14d3 52
66abcbb0
JF
53#if 0
54#define DEPLOYMENT_TARGET_MACOSX 1
55#define CF_BUILDING_CF 1
56#include <CoreFoundation/CFInternal.h>
57#endif
58
43b742af
JF
59#include <CoreFoundation/CFPriv.h>
60#include <CoreFoundation/CFUniChar.h>
61
d7a235a6
JF
62#include <SystemConfiguration/SystemConfiguration.h>
63
1e4922b8
JF
64#include <UIKit/UIKit.h>
65#include "iPhonePrivate.h"
66
67#include <IOKit/IOKitLib.h>
d210b85d 68
6932575e 69#include <WebCore/WebCoreThread.h>
77f175ac 70
43b742af 71#include <algorithm>
3bddda52 72#include <iomanip>
4941f41d 73#include <sstream>
8993ad57
JF
74#include <string>
75
4941f41d
JF
76#include <ext/stdio_filebuf.h>
77
7805b429
JF
78#undef ABS
79
a75f53e7
JF
80#include <apt-pkg/acquire.h>
81#include <apt-pkg/acquire-item.h>
82#include <apt-pkg/algorithms.h>
83#include <apt-pkg/cachefile.h>
d72d91aa 84#include <apt-pkg/clean.h>
a75f53e7 85#include <apt-pkg/configuration.h>
3e3977a2 86#include <apt-pkg/debindexfile.h>
2d28b35a 87#include <apt-pkg/debmetaindex.h>
a75f53e7
JF
88#include <apt-pkg/error.h>
89#include <apt-pkg/init.h>
1eb0c554 90#include <apt-pkg/mmap.h>
a75f53e7 91#include <apt-pkg/pkgrecords.h>
1eb0c554 92#include <apt-pkg/sha1.h>
a75f53e7 93#include <apt-pkg/sourcelist.h>
4941f41d 94#include <apt-pkg/sptr.h>
f464053e 95#include <apt-pkg/strutl.h>
b8b1cfd0 96#include <apt-pkg/tagfile.h>
a75f53e7 97
6932575e
JF
98#include <apr-1/apr_pools.h>
99
2cb68ddf
JF
100#include <sys/types.h>
101#include <sys/stat.h>
2d28b35a 102#include <sys/sysctl.h>
65fe894c
JF
103#include <sys/param.h>
104#include <sys/mount.h>
2cb68ddf 105
1e4922b8 106#include <fcntl.h>
fc675b93 107#include <notify.h>
1eb0c554 108#include <dlfcn.h>
2d28b35a 109
686e302f
JF
110extern "C" {
111#include <mach-o/nlist.h>
112}
113
8993ad57
JF
114#include <cstdio>
115#include <cstdlib>
116#include <cstring>
686e302f 117
4941f41d 118#include <errno.h>
a75f53e7 119#include <pcre.h>
d210b85d 120
1e4922b8 121#include "UICaboodle/BrowserView.h"
2bfeaf6f 122
1e4922b8 123#include "substrate.h"
9ae52960
GP
124/* }}} */
125
017b2b71 126/* Profiler {{{ */
f159ecd4
JF
127struct timeval _ltv;
128bool _itv;
129
e2a207dd
JF
130#define _timestamp ({ \
131 struct timeval tv; \
132 gettimeofday(&tv, NULL); \
133 tv.tv_sec * 1000000 + tv.tv_usec; \
134})
135
3bddda52
JF
136typedef std::vector<class ProfileTime *> TimeList;
137TimeList times_;
138
139class ProfileTime {
140 private:
141 const char *name_;
142 uint64_t total_;
142bd2db 143 uint64_t count_;
3bddda52
JF
144
145 public:
146 ProfileTime(const char *name) :
147 name_(name),
148 total_(0)
149 {
150 times_.push_back(this);
151 }
152
153 void AddTime(uint64_t time) {
154 total_ += time;
142bd2db 155 ++count_;
3bddda52
JF
156 }
157
158 void Print() {
159 if (total_ != 0)
142bd2db 160 std::cerr << std::setw(5) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl;
3bddda52 161 total_ = 0;
142bd2db 162 count_ = 0;
3bddda52
JF
163 }
164};
165
166class ProfileTimer {
167 private:
168 ProfileTime &time_;
169 uint64_t start_;
170
171 public:
172 ProfileTimer(ProfileTime &time) :
173 time_(time),
174 start_(_timestamp)
175 {
176 }
177
178 ~ProfileTimer() {
179 time_.AddTime(_timestamp - start_);
180 }
181};
182
183void PrintTimes() {
184 for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i)
185 (*i)->Print();
186 std::cerr << "========" << std::endl;
187}
188
189#define _profile(name) { \
190 static ProfileTime name(#name); \
191 ProfileTimer _ ## name(name);
192
193#define _end }
6932575e 194/* }}} */
f464053e
JF
195
196#define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
197
7831584c
JF
198#define CYPoolStart() \
199 NSAutoreleasePool *_pool([[NSAutoreleasePool alloc] init]); \
200 do
201#define CYPoolEnd() \
202 while (false); \
203 [_pool release];
204
d3bef7bc
JF
205static const NSUInteger UIViewAutoresizingFlexibleBoth(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
206
541a556a
JF
207void NSLogPoint(const char *fix, const CGPoint &point) {
208 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
209}
210
f464053e
JF
211void NSLogRect(const char *fix, const CGRect &rect) {
212 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
c045fc11
JF
213}
214
6981ccdf
JF
215static _finline NSString *CydiaURL(NSString *path) {
216 char page[25];
217 page[0] = 'h'; page[1] = 't'; page[2] = 't'; page[3] = 'p'; page[4] = ':';
218 page[5] = '/'; page[6] = '/'; page[7] = 'c'; page[8] = 'y'; page[9] = 'd';
219 page[10] = 'i'; page[11] = 'a'; page[12] = '.'; page[13] = 's'; page[14] = 'a';
220 page[15] = 'u'; page[16] = 'r'; page[17] = 'i'; page[18] = 'k'; page[19] = '.';
221 page[20] = 'c'; page[21] = 'o'; page[22] = 'm'; page[23] = '/'; page[24] = '\0';
222 return [[NSString stringWithUTF8String:page] stringByAppendingString:path];
223}
224
864da84b
RP
225static _finline void UpdateExternalStatus(uint64_t newStatus) {
226 int notify_token;
227 if (notify_register_check("com.saurik.Cydia.status", &notify_token) == NOTIFY_STATUS_OK) {
228 notify_set_state(notify_token, newStatus);
229 notify_cancel(notify_token);
230 }
231 notify_post("com.saurik.Cydia.status");
232}
233
017b2b71 234/* [NSObject yieldToSelector:(withObject:)] {{{*/
7398a389 235@interface NSObject (Cydia)
b4dff19a
JF
236- (id) yieldToSelector:(SEL)selector withObject:(id)object;
237- (id) yieldToSelector:(SEL)selector;
7398a389
JF
238@end
239
240@implementation NSObject (Cydia)
241
242- (void) doNothing {
243}
244
b4dff19a 245- (void) _yieldToContext:(NSMutableArray *)context { _pooled
7398a389
JF
246 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
247 id object([[context objectAtIndex:1] nonretainedObjectValue]);
248 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
249
b4dff19a
JF
250 /* XXX: deal with exceptions */
251 id value([self performSelector:selector withObject:object]);
252
43b742af 253 NSMethodSignature *signature([self methodSignatureForSelector:selector]);
b4dff19a 254 [context removeAllObjects];
43b742af 255 if ([signature methodReturnLength] != 0 && value != nil)
b4dff19a 256 [context addObject:value];
7398a389
JF
257
258 stopped = true;
259
260 [self
261 performSelectorOnMainThread:@selector(doNothing)
262 withObject:nil
263 waitUntilDone:NO
264 ];
265}
266
b4dff19a 267- (id) yieldToSelector:(SEL)selector withObject:(id)object {
7b00c562 268 /*return [self performSelector:selector withObject:object];*/
3bddda52 269
7398a389
JF
270 volatile bool stopped(false);
271
b4dff19a 272 NSMutableArray *context([NSMutableArray arrayWithObjects:
7398a389
JF
273 [NSValue valueWithPointer:selector],
274 [NSValue valueWithNonretainedObject:object],
275 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
276 nil]);
277
278 NSThread *thread([[[NSThread alloc]
279 initWithTarget:self
280 selector:@selector(_yieldToContext:)
281 object:context
282 ] autorelease]);
283
284 [thread start];
285
286 NSRunLoop *loop([NSRunLoop currentRunLoop]);
287 NSDate *future([NSDate distantFuture]);
288
289 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
b4dff19a
JF
290
291 return [context count] == 0 ? nil : [context objectAtIndex:0];
292}
293
294- (id) yieldToSelector:(SEL)selector {
295 return [self yieldToSelector:selector withObject:nil];
7398a389
JF
296}
297
298@end
017b2b71 299/* }}} */
7398a389 300
6744338a 301/* Cydia Action Sheet {{{ */
19723386 302@interface CYActionSheet : UIAlertView {
6981ccdf
JF
303 unsigned button_;
304}
305
306- (int) yieldToPopupAlertAnimated:(BOOL)animated;
307@end
308
309@implementation CYActionSheet
310
311- (id) initWithTitle:(NSString *)title buttons:(NSArray *)buttons defaultButtonIndex:(int)index {
19723386 312 if ((self = [super init])) {
9ae52960
GP
313 [self setTitle:title];
314 [self setDelegate:self];
315 for (NSString *button in buttons) [self addButtonWithTitle:button];
316 [self setCancelButtonIndex:index];
6981ccdf
JF
317 } return self;
318}
319
d5653f96 320- (void) _updateFrameForDisplay {
8b6bb0fd
RP
321 [super _updateFrameForDisplay];
322 if ([self cancelButtonIndex] == -1) {
323 NSArray *buttons = [self buttons];
324 if ([buttons count]) {
325 UIImage *background = [[buttons objectAtIndex:0] backgroundForState:0];
326 for (UIThreePartButton *button in buttons)
327 [button setBackground:background forState:0];
328 }
329 }
330}
331
19723386
GP
332- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
333 button_ = buttonIndex + 1;
6981ccdf
JF
334}
335
9e639c5a 336- (void) dismiss {
9ae52960 337 [self dismissWithClickedButtonIndex:-1 animated:YES];
9e639c5a
GP
338}
339
6981ccdf 340- (int) yieldToPopupAlertAnimated:(BOOL)animated {
05636a2c 341 [self setRunsModal:YES];
6981ccdf 342 button_ = 0;
9e639c5a 343 [self show];
6981ccdf
JF
344 return button_;
345}
346
347@end
6744338a 348/* }}} */
6981ccdf 349
9c4e0cbe 350/* NSForcedOrderingSearch doesn't work on the iPhone */
3bddda52 351static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
43b742af
JF
352static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch;
353static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareLocalized | kCFCompareNumerically | kCFCompareWidthInsensitive | kCFCompareForcedOrdering;
c045fc11 354
7f9c29fa
JF
355/* Information Dictionaries {{{ */
356@interface NSMutableArray (Cydia)
357- (void) addInfoDictionary:(NSDictionary *)info;
358@end
359
360@implementation NSMutableArray (Cydia)
361
362- (void) addInfoDictionary:(NSDictionary *)info {
363 [self addObject:info];
364}
365
366@end
367
368@interface NSMutableDictionary (Cydia)
369- (void) addInfoDictionary:(NSDictionary *)info;
370@end
371
372@implementation NSMutableDictionary (Cydia)
373
374- (void) addInfoDictionary:(NSDictionary *)info {
017b2b71 375 [self setObject:info forKey:[info objectForKey:@"CFBundleIdentifier"]];
7f9c29fa
JF
376}
377
378@end
379/* }}} */
380
cb9c2100 381#define lprintf(args...) fprintf(stderr, args)
d210b85d 382
982de8f1 383#define ForRelease 1
1ff58dbb 384#define TraceLogging (1 && !ForRelease)
2df365d8 385#define HistogramInsertionSort (!ForRelease ? 0 : 0)
b8b1cfd0 386#define ProfileTimes (0 && !ForRelease)
fa5cb337 387#define ForSaurik (0 && !ForRelease)
6981ccdf 388#define LogBrowser (0 && !ForRelease)
bb9edf8b 389#define TrackResize (0 && !ForRelease)
c2292b80 390#define ManualRefresh (1 && !ForRelease)
6932575e 391#define ShowInternals (0 && !ForRelease)
83105e6e 392#define IgnoreInstall (0 && !ForRelease)
c2292b80 393#define AlwaysReload (0 && !ForRelease)
81ab76dc 394
1ff58dbb 395#if !TraceLogging
6e673d99
JF
396#undef _trace
397#define _trace(args...)
1ff58dbb
JF
398#endif
399
400#if !ProfileTimes
3bddda52 401#undef _profile
7b00c562 402#define _profile(name) {
3bddda52 403#undef _end
7b00c562 404#define _end }
b4dff19a 405#define PrintTimes() do {} while (false)
6e673d99
JF
406#endif
407
f159ecd4 408/* Radix Sort {{{ */
dd9390c5
JF
409typedef uint32_t (*SKRadixFunction)(id, void *);
410
f159ecd4 411@interface NSMutableArray (Radix)
dd9390c5 412- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument;
f159ecd4
JF
413@end
414
6932575e
JF
415struct RadixItem_ {
416 size_t index;
417 uint32_t key;
418};
f159ecd4 419
ac11599c
JF
420@implementation NSMutableArray (Radix)
421
422- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument {
423 size_t count([self count]);
424 struct RadixItem_ *swap(new RadixItem_[count * 2]);
425
426 for (size_t i(0); i != count; ++i) {
427 RadixItem_ &item(swap[i]);
428 item.index = i;
429
430 id object([self objectAtIndex:i]);
431 item.key = function(object, argument);
432 }
433
6932575e 434 struct RadixItem_ *lhs(swap), *rhs(swap + count);
f159ecd4 435
890c1d38 436 static const size_t width = 32;
f159ecd4
JF
437 static const size_t bits = 11;
438 static const size_t slots = 1 << bits;
890c1d38 439 static const size_t passes = (width + (bits - 1)) / bits;
f159ecd4
JF
440
441 size_t *hist(new size_t[slots]);
442
443 for (size_t pass(0); pass != passes; ++pass) {
444 memset(hist, 0, sizeof(size_t) * slots);
445
446 for (size_t i(0); i != count; ++i) {
447 uint32_t key(lhs[i].key);
448 key >>= pass * bits;
890c1d38 449 key &= _not(uint32_t) >> width - bits;
f159ecd4
JF
450 ++hist[key];
451 }
452
453 size_t offset(0);
454 for (size_t i(0); i != slots; ++i) {
455 size_t local(offset);
456 offset += hist[i];
457 hist[i] = local;
458 }
459
460 for (size_t i(0); i != count; ++i) {
461 uint32_t key(lhs[i].key);
462 key >>= pass * bits;
890c1d38 463 key &= _not(uint32_t) >> width - bits;
f159ecd4
JF
464 rhs[hist[key]++] = lhs[i];
465 }
466
6932575e 467 RadixItem_ *tmp(lhs);
f159ecd4
JF
468 lhs = rhs;
469 rhs = tmp;
470 }
471
472 delete [] hist;
473
2ec90f7b 474 const void **values(new const void *[count]);
f159ecd4 475 for (size_t i(0); i != count; ++i)
2ec90f7b
JF
476 values[i] = [self objectAtIndex:lhs[i].index];
477 CFArrayReplaceValues((CFMutableArrayRef) self, CFRangeMake(0, count), values, count);
478 delete [] values;
f159ecd4
JF
479
480 delete [] swap;
481}
482
483@end
66abcbb0
JF
484/* }}} */
485/* Insertion Sort {{{ */
486
dd9390c5
JF
487CFIndex SKBSearch_(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) {
488 const char *ptr = (const char *)list;
489 while (0 < count) {
490 CFIndex half = count / 2;
491 const char *probe = ptr + elementSize * half;
492 CFComparisonResult cr = comparator(element, probe, context);
9ae52960 493 if (0 == cr) return (probe - (const char *)list) / elementSize;
dd9390c5
JF
494 ptr = (cr < 0) ? ptr : probe + elementSize;
495 count = (cr < 0) ? half : (half + (count & 1) - 1);
496 }
497 return (ptr - (const char *)list) / elementSize;
498}
499
66abcbb0
JF
500CFIndex CFBSearch_(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) {
501 const char *ptr = (const char *)list;
502 while (0 < count) {
503 CFIndex half = count / 2;
504 const char *probe = ptr + elementSize * half;
505 CFComparisonResult cr = comparator(element, probe, context);
9ae52960 506 if (0 == cr) return (probe - (const char *)list) / elementSize;
66abcbb0
JF
507 ptr = (cr < 0) ? ptr : probe + elementSize;
508 count = (cr < 0) ? half : (half + (count & 1) - 1);
509 }
510 return (ptr - (const char *)list) / elementSize;
511}
512
513void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunction comparator, void *context) {
514 if (range.length == 0)
515 return;
516 const void **values(new const void *[range.length]);
517 CFArrayGetValues(array, range, values);
518
2df365d8 519#if HistogramInsertionSort > 0
dd9390c5
JF
520 uint32_t total(0), *offsets(new uint32_t[range.length]);
521#endif
522
66abcbb0
JF
523 for (CFIndex index(1); index != range.length; ++index) {
524 const void *value(values[index]);
dd9390c5
JF
525 //CFIndex correct(SKBSearch_(&value, sizeof(const void *), values, index, comparator, context));
526 CFIndex correct(index);
2df365d8
JF
527 while (comparator(value, values[correct - 1], context) == kCFCompareLessThan) {
528#if HistogramInsertionSort > 1
529 NSLog(@"%@ < %@", value, values[correct - 1]);
530#endif
dd9390c5
JF
531 if (--correct == 0)
532 break;
2df365d8 533 }
66abcbb0 534 if (correct != index) {
dd9390c5
JF
535 size_t offset(index - correct);
536#if HistogramInsertionSort
537 total += offset;
538 ++offsets[offset];
539 if (offset > 10)
540 NSLog(@"Heavy Insertion Displacement: %u = %@", offset, value);
541#endif
542 memmove(values + correct + 1, values + correct, sizeof(const void *) * offset);
66abcbb0
JF
543 values[correct] = value;
544 }
545 }
546
547 CFArrayReplaceValues(array, range, values, range.length);
548 delete [] values;
dd9390c5 549
2df365d8 550#if HistogramInsertionSort > 0
dd9390c5
JF
551 for (CFIndex index(0); index != range.length; ++index)
552 if (offsets[index] != 0)
553 NSLog(@"Insertion Displacement [%u]: %u", index, offsets[index]);
554 NSLog(@"Average Insertion Displacement: %f", double(total) / range.length);
555 delete [] offsets;
556#endif
66abcbb0
JF
557}
558
f159ecd4
JF
559/* }}} */
560
541a556a
JF
561/* Apple Bug Fixes {{{ */
562@implementation UIWebDocumentView (Cydia)
563
564- (void) _setScrollerOffset:(CGPoint)offset {
565 UIScroller *scroller([self _scroller]);
566
567 CGSize size([scroller contentSize]);
568 CGSize bounds([scroller bounds].size);
569
570 CGPoint max;
571 max.x = size.width - bounds.width;
572 max.y = size.height - bounds.height;
573
574 // wtf Apple?!
575 if (max.x < 0)
576 max.x = 0;
577 if (max.y < 0)
578 max.y = 0;
579
580 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
581 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
582
583 [scroller setOffset:offset];
584}
585
586@end
587/* }}} */
588
8a4106c2
JF
589@implementation WebScriptObject (NSFastEnumeration)
590
591- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)objects count:(NSUInteger)count {
daf7f6e2
JF
592 size_t length([self count] - state->state);
593 if (length <= 0)
594 return 0;
595 else if (length > count)
596 length = count;
597 for (size_t i(0); i != length; ++i)
598 objects[i] = [self objectAtIndex:state->state++];
599 state->itemsPtr = objects;
600 state->mutationsPtr = (unsigned long *) self;
601 return length;
602}
603
8a4106c2
JF
604@end
605
cd8bec2f
JF
606NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
607 size_t length([self length] - state->state);
608 if (length <= 0)
609 return 0;
610 else if (length > count)
611 length = count;
612 for (size_t i(0); i != length; ++i)
613 objects[i] = [self item:state->state++];
614 state->itemsPtr = objects;
615 state->mutationsPtr = (unsigned long *) self;
616 return length;
617}
618
017b2b71 619/* Cydia NSString Additions {{{ */
2a987aa5 620@interface NSString (Cydia)
3bddda52 621+ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
6932575e 622+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length withZone:(NSZone *)zone inPool:(apr_pool_t *)pool;
2a987aa5 623+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
9e07091a 624- (NSComparisonResult) compareByPath:(NSString *)other;
b4dff19a
JF
625- (NSString *) stringByCachingURLWithCurrentCDN;
626- (NSString *) stringByAddingPercentEscapesIncludingReserved;
2a987aa5
JF
627@end
628
629@implementation NSString (Cydia)
630
3bddda52
JF
631+ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
632 return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
633}
634
6932575e
JF
635+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length withZone:(NSZone *)zone inPool:(apr_pool_t *)pool {
636 char *data(reinterpret_cast<char *>(apr_palloc(pool, length)));
637 memcpy(data, bytes, length);
638 return [[[NSString allocWithZone:zone] initWithBytesNoCopy:data length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
639}
640
2a987aa5 641+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
e2a207dd 642 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
2a987aa5
JF
643}
644
9e07091a
JF
645- (NSComparisonResult) compareByPath:(NSString *)other {
646 NSString *prefix = [self commonPrefixWithString:other options:0];
647 size_t length = [prefix length];
648
649 NSRange lrange = NSMakeRange(length, [self length] - length);
650 NSRange rrange = NSMakeRange(length, [other length] - length);
651
652 lrange = [self rangeOfString:@"/" options:0 range:lrange];
653 rrange = [other rangeOfString:@"/" options:0 range:rrange];
654
655 NSComparisonResult value;
656
657 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
658 value = NSOrderedSame;
659 else if (lrange.location == NSNotFound)
660 value = NSOrderedAscending;
661 else if (rrange.location == NSNotFound)
662 value = NSOrderedDescending;
663 else
664 value = NSOrderedSame;
665
666 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
667 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
668 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
669 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
670
671 NSComparisonResult result = [lpath compare:rpath];
672 return result == NSOrderedSame ? value : result;
673}
674
b4dff19a
JF
675- (NSString *) stringByCachingURLWithCurrentCDN {
676 return [self
99dc9e91
JF
677 stringByReplacingOccurrencesOfString:@"://cydia.saurik.com/"
678 withString:@"://cache.cydia.saurik.com/"
b4dff19a
JF
679 ];
680}
681
682- (NSString *) stringByAddingPercentEscapesIncludingReserved {
683 return [(id)CFURLCreateStringByAddingPercentEscapes(
f99f86e2 684 kCFAllocatorDefault,
b4dff19a
JF
685 (CFStringRef) self,
686 NULL,
687 CFSTR(";/?:@&=+$,"),
688 kCFStringEncodingUTF8
689 ) autorelease];
690}
691
2a987aa5 692@end
017b2b71 693/* }}} */
2a987aa5 694
017b2b71 695/* C++ NSString Wrapper Cache {{{ */
2a0a85d0
JF
696static _finline CFStringRef CYStringCreate(const char *data, size_t size) {
697 return size == 0 ? NULL :
698 CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(data), size, kCFStringEncodingUTF8, NO, kCFAllocatorNull) ?:
699 CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(data), size, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull);
700}
701
702static _finline CFStringRef CYStringCreate(const char *data) {
703 return CYStringCreate(data, strlen(data));
704}
705
6932575e
JF
706class CYString {
707 private:
708 char *data_;
709 size_t size_;
710 CFStringRef cache_;
711
712 _finline void clear_() {
f30eaf83 713 if (cache_ != NULL) {
6932575e 714 CFRelease(cache_);
f30eaf83
JF
715 cache_ = NULL;
716 }
6932575e
JF
717 }
718
719 public:
720 _finline bool empty() const {
721 return size_ == 0;
722 }
723
724 _finline size_t size() const {
725 return size_;
726 }
727
728 _finline char *data() const {
729 return data_;
730 }
731
732 _finline void clear() {
733 size_ = 0;
734 clear_();
735 }
736
737 _finline CYString() :
738 data_(0),
739 size_(0),
f30eaf83 740 cache_(NULL)
6932575e
JF
741 {
742 }
743
744 _finline ~CYString() {
745 clear_();
746 }
747
748 void operator =(const CYString &rhs) {
749 data_ = rhs.data_;
750 size_ = rhs.size_;
751
752 if (rhs.cache_ == nil)
753 cache_ = NULL;
754 else
755 cache_ = reinterpret_cast<CFStringRef>(CFRetain(rhs.cache_));
756 }
757
758 void set(apr_pool_t *pool, const char *data, size_t size) {
759 if (size == 0)
760 clear();
761 else {
762 clear_();
763
9ee296df 764 char *temp(reinterpret_cast<char *>(apr_palloc(pool, size + 1)));
6932575e 765 memcpy(temp, data, size);
9ee296df 766 temp[size] = '\0';
6932575e
JF
767 data_ = temp;
768 size_ = size;
769 }
770 }
771
772 _finline void set(apr_pool_t *pool, const char *data) {
773 set(pool, data, data == NULL ? 0 : strlen(data));
774 }
775
776 _finline void set(apr_pool_t *pool, const std::string &rhs) {
777 set(pool, rhs.data(), rhs.size());
778 }
779
780 bool operator ==(const CYString &rhs) const {
781 return size_ == rhs.size_ && memcmp(data_, rhs.data_, size_) == 0;
782 }
783
2a0a85d0
JF
784 _finline operator CFStringRef() {
785 if (cache_ == NULL)
786 cache_ = CYStringCreate(data_, size_);
787 return cache_;
dd9390c5
JF
788 }
789
790 _finline operator id() {
791 return (NSString *) static_cast<CFStringRef>(*this);
6932575e
JF
792 }
793};
017b2b71
JF
794/* }}} */
795/* C++ NSString Algorithm Adapters {{{ */
6932575e
JF
796extern "C" {
797 CF_EXPORT CFHashCode CFStringHashNSString(CFStringRef str);
798}
799
800struct NSStringMapHash :
801 std::unary_function<NSString *, size_t>
802{
803 _finline size_t operator ()(NSString *value) const {
804 return CFStringHashNSString((CFStringRef) value);
805 }
806};
807
808struct NSStringMapLess :
809 std::binary_function<NSString *, NSString *, bool>
810{
811 _finline bool operator ()(NSString *lhs, NSString *rhs) const {
812 return [lhs compare:rhs] == NSOrderedAscending;
813 }
814};
815
816struct NSStringMapEqual :
817 std::binary_function<NSString *, NSString *, bool>
818{
819 _finline bool operator ()(NSString *lhs, NSString *rhs) const {
820 return CFStringCompare((CFStringRef) lhs, (CFStringRef) rhs, 0) == kCFCompareEqualTo;
821 //CFEqual((CFTypeRef) lhs, (CFTypeRef) rhs);
822 //[lhs isEqualToString:rhs];
823 }
824};
017b2b71 825/* }}} */
6932575e 826
686e302f
JF
827/* Perl-Compatible RegEx {{{ */
828class Pcre {
829 private:
830 pcre *code_;
831 pcre_extra *study_;
832 int capture_;
833 int *matches_;
834 const char *data_;
835
836 public:
837 Pcre(const char *regex) :
838 study_(NULL)
839 {
840 const char *error;
841 int offset;
842 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
843
844 if (code_ == NULL) {
cb9c2100 845 lprintf("%d:%s\n", offset, error);
686e302f
JF
846 _assert(false);
847 }
848
849 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
850 matches_ = new int[(capture_ + 1) * 3];
851 }
852
853 ~Pcre() {
854 pcre_free(code_);
855 delete matches_;
856 }
857
858 NSString *operator [](size_t match) {
2a987aa5 859 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
686e302f
JF
860 }
861
7600bd69
JF
862 bool operator ()(NSString *data) {
863 // XXX: length is for characters, not for bytes
864 return operator ()([data UTF8String], [data length]);
865 }
866
686e302f
JF
867 bool operator ()(const char *data, size_t size) {
868 data_ = data;
869 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
870 }
871};
872/* }}} */
e057ec05 873/* Mime Addresses {{{ */
e057ec05
JF
874@interface Address : NSObject {
875 NSString *name_;
ad554f10 876 NSString *address_;
e057ec05
JF
877}
878
879- (NSString *) name;
ad554f10 880- (NSString *) address;
e057ec05 881
3ff1504e
JF
882- (void) setAddress:(NSString *)address;
883
e057ec05
JF
884+ (Address *) addressWithString:(NSString *)string;
885- (Address *) initWithString:(NSString *)string;
886@end
887
888@implementation Address
889
890- (void) dealloc {
891 [name_ release];
ad554f10
JF
892 if (address_ != nil)
893 [address_ release];
e057ec05
JF
894 [super dealloc];
895}
896
897- (NSString *) name {
898 return name_;
899}
900
ad554f10
JF
901- (NSString *) address {
902 return address_;
e057ec05
JF
903}
904
3ff1504e
JF
905- (void) setAddress:(NSString *)address {
906 if (address_ != nil)
907 [address_ autorelease];
908 if (address == nil)
909 address_ = nil;
910 else
911 address_ = [address retain];
912}
913
e057ec05
JF
914+ (Address *) addressWithString:(NSString *)string {
915 return [[[Address alloc] initWithString:string] autorelease];
916}
917
ad554f10
JF
918+ (NSArray *) _attributeKeys {
919 return [NSArray arrayWithObjects:@"address", @"name", nil];
920}
921
922- (NSArray *) attributeKeys {
923 return [[self class] _attributeKeys];
924}
925
926+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
927 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
928}
929
e057ec05
JF
930- (Address *) initWithString:(NSString *)string {
931 if ((self = [super init]) != nil) {
932 const char *data = [string UTF8String];
933 size_t size = [string length];
934
ad554f10 935 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
faf4eb4f 936
ad554f10
JF
937 if (address_r(data, size)) {
938 name_ = [address_r[1] retain];
939 address_ = [address_r[2] retain];
e057ec05 940 } else {
f464053e 941 name_ = [string retain];
ad554f10 942 address_ = nil;
e057ec05
JF
943 }
944 } return self;
945}
946
947@end
948/* }}} */
b0d03ade 949/* CoreGraphics Primitives {{{ */
eb6d9c93 950class CYColor {
686e302f
JF
951 private:
952 CGColorRef color_;
953
954 public:
eb6d9c93 955 CYColor() :
e057ec05
JF
956 color_(NULL)
957 {
958 }
959
eb6d9c93 960 CYColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
e057ec05
JF
961 color_(NULL)
962 {
963 Set(space, red, green, blue, alpha);
964 }
965
966 void Clear() {
967 if (color_ != NULL)
968 CGColorRelease(color_);
686e302f
JF
969 }
970
eb6d9c93 971 ~CYColor() {
e057ec05
JF
972 Clear();
973 }
974
975 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
976 Clear();
977 float color[] = {red, green, blue, alpha};
9b62701b 978 color_ = CGColorCreate(space, (CGFloat *) color);
686e302f
JF
979 }
980
981 operator CGColorRef() {
982 return color_;
983 }
984};
686e302f
JF
985/* }}} */
986
e057ec05 987/* Random Global Variables {{{ */
2d28b35a 988static const int PulseInterval_ = 50000;
575ffd3c 989static const int ButtonBarWidth_ = 60;
2a987aa5 990static const int ButtonBarHeight_ = 48;
fa7bb92f 991static const float KeyboardTime_ = 0.3f;
f464053e 992
c59881cd
JF
993static int Finish_;
994static NSArray *Finishes_;
995
f464053e 996#define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
bde2d79b 997#define NotifyConfig_ "/etc/notify.conf"
fc675b93 998
3ff1504e
JF
999static bool Queuing_;
1000
eb6d9c93
JF
1001static CYColor Blue_;
1002static CYColor Blueish_;
1003static CYColor Black_;
1004static CYColor Off_;
1005static CYColor White_;
1006static CYColor Gray_;
1007static CYColor Green_;
1008static CYColor Purple_;
1009static CYColor Purplish_;
d8d9a65c 1010
3ff1504e
JF
1011static UIColor *InstallingColor_;
1012static UIColor *RemovingColor_;
e057ec05 1013
853d14d3 1014static NSString *App_;
0039464f 1015static NSString *Home_;
0039464f 1016
2a987aa5 1017static BOOL Advanced_;
9e07091a 1018static BOOL Ignored_;
2a987aa5 1019
5e563e79
JF
1020static UIFont *Font12_;
1021static UIFont *Font12Bold_;
1022static UIFont *Font14_;
1023static UIFont *Font18Bold_;
1024static UIFont *Font22Bold_;
1025
2cb68ddf 1026static const char *Machine_ = NULL;
78f1a54b 1027static NSString *System_ = nil;
3bdadc1e
JF
1028static NSString *SerialNumber_ = nil;
1029static NSString *ChipID_ = nil;
1030static NSString *Token_ = nil;
1031static NSString *UniqueID_ = nil;
3074466a 1032static NSString *PLMN_ = nil;
3bdadc1e
JF
1033static NSString *Build_ = nil;
1034static NSString *Product_ = nil;
1035static NSString *Safari_ = nil;
fc675b93 1036
c59881cd
JF
1037static CFLocaleRef Locale_;
1038static NSArray *Languages_;
1039static CGColorSpaceRef space_;
e057ec05 1040
d1494d2c 1041static NSDictionary *SectionMap_;
686e302f 1042static NSMutableDictionary *Metadata_;
faf4eb4f
JF
1043static _transient NSMutableDictionary *Settings_;
1044static _transient NSString *Role_;
1045static _transient NSMutableDictionary *Packages_;
1046static _transient NSMutableDictionary *Sections_;
1047static _transient NSMutableDictionary *Sources_;
3d37fc0d 1048static bool Changed_;
686e302f 1049static NSDate *now_;
20dd7407 1050
5ec44e34 1051static bool IsWildcat_;
e057ec05 1052/* }}} */
c59881cd 1053
e057ec05
JF
1054/* Display Helpers {{{ */
1055inline float Interpolate(float begin, float end, float fraction) {
1056 return (end - begin) * fraction + begin;
1057}
4941f41d 1058
6932575e 1059/* XXX: localize this! */
b6ffa083 1060NSString *SizeString(double size) {
f464053e
JF
1061 bool negative = size < 0;
1062 if (negative)
1063 size = -size;
1064
b6ffa083
JF
1065 unsigned power = 0;
1066 while (size > 1024) {
1067 size /= 1024;
1068 ++power;
1069 }
1070
1071 static const char *powers_[] = {"B", "kB", "MB", "GB"};
1072
56e10908 1073 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
b6ffa083
JF
1074}
1075
5bb2842a 1076static _finline const char *StripVersion_(const char *version) {
b4c4fac4
JF
1077 const char *colon(strchr(version, ':'));
1078 if (colon != NULL)
1079 version = colon + 1;
1080 return version;
1081}
1082
6932575e 1083NSString *LocalizeSection(NSString *section) {
bb9edf8b 1084 static Pcre title_r("^(.*?) \\((.*)\\)$");
9ee296df
JF
1085 if (title_r(section)) {
1086 NSString *parent(title_r[1]);
1087 NSString *child(title_r[2]);
1088
61b13cae 1089 return [NSString stringWithFormat:UCLocalize("PARENTHETICAL"),
9ee296df
JF
1090 LocalizeSection(parent),
1091 LocalizeSection(child)
bb9edf8b 1092 ];
9ee296df 1093 }
bb9edf8b
JF
1094
1095 return [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"];
6932575e
JF
1096}
1097
49525fb2
JF
1098NSString *Simplify(NSString *title) {
1099 const char *data = [title UTF8String];
1100 size_t size = [title length];
1101
faf4eb4f
JF
1102 static Pcre square_r("^\\[(.*)\\]$");
1103 if (square_r(data, size))
1104 return Simplify(square_r[1]);
1105
1106 static Pcre paren_r("^\\((.*)\\)$");
1107 if (paren_r(data, size))
1108 return Simplify(paren_r[1]);
1109
bb9edf8b 1110 static Pcre title_r("^(.*?) \\((.*)\\)$");
49525fb2 1111 if (title_r(data, size))
faf4eb4f
JF
1112 return Simplify(title_r[1]);
1113
1114 return title;
49525fb2 1115}
e057ec05
JF
1116/* }}} */
1117
c59881cd
JF
1118NSString *GetLastUpdate() {
1119 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
1120
1121 if (update == nil)
1122 return UCLocalize("NEVER_OR_UNKNOWN");
1123
1124 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
1125 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
1126
1127 CFRelease(formatter);
1128
1129 return [(NSString *) formatted autorelease];
1130}
1131
fa7bb92f 1132bool isSectionVisible(NSString *section) {
43b742af
JF
1133 NSDictionary *metadata([Sections_ objectForKey:section]);
1134 NSNumber *hidden(metadata == nil ? nil : [metadata objectForKey:@"Hidden"]);
fa7bb92f
JF
1135 return hidden == nil || ![hidden boolValue];
1136}
1137
15b41c77
GP
1138@class Cydia;
1139
a0e1b888 1140/* Delegate Prototypes {{{ */
e057ec05
JF
1141@class Package;
1142@class Source;
1143
238b07ce
JF
1144@interface NSObject (ProgressDelegate)
1145@end
1146
e057ec05 1147@protocol ProgressDelegate
6981ccdf 1148- (void) setProgressError:(NSString *)error withTitle:(NSString *)id;
e057ec05
JF
1149- (void) setProgressTitle:(NSString *)title;
1150- (void) setProgressPercent:(float)percent;
87c76914 1151- (void) startProgress;
e057ec05 1152- (void) addProgressOutput:(NSString *)output;
87c76914 1153- (bool) isCancelling:(size_t)received;
e057ec05
JF
1154@end
1155
7600bd69 1156@protocol ConfigurationDelegate
965edd52 1157- (void) repairWithSelector:(SEL)selector;
7600bd69
JF
1158- (void) setConfigurationData:(NSString *)data;
1159@end
1160
f441e717 1161@class PackageController;
6932575e 1162
e057ec05 1163@protocol CydiaDelegate
f441e717 1164- (void) setPackageController:(PackageController *)view;
3ff1504e 1165- (void) clearPackage:(Package *)package;
e057ec05 1166- (void) installPackage:(Package *)package;
daf7f6e2 1167- (void) installPackages:(NSArray *)packages;
e057ec05 1168- (void) removePackage:(Package *)package;
1e4922b8
JF
1169- (void) beginUpdate;
1170- (BOOL) updating;
e057ec05 1171- (void) distUpgrade;
90e9a238 1172- (void) loadData;
fa7bb92f 1173- (void) updateData;
faf4eb4f 1174- (void) syncData;
c1edf105 1175- (void) showSettings;
faf4eb4f 1176- (UIProgressHUD *) addProgressHUD;
832c0799 1177- (BOOL) hudIsShowing;
7398a389 1178- (void) removeProgressHUD:(UIProgressHUD *)hud;
881fe77f 1179- (CYViewController *) pageForPackage:(NSString *)name;
f441e717 1180- (PackageController *) packageController;
48861ec9 1181- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item;
e057ec05 1182@end
a0e1b888 1183/* }}} */
686e302f 1184
a75f53e7
JF
1185/* Status Delegation {{{ */
1186class Status :
1187 public pkgAcquireStatus
1188{
1189 private:
238b07ce 1190 _transient NSObject<ProgressDelegate> *delegate_;
a75f53e7
JF
1191
1192 public:
1193 Status() :
1194 delegate_(nil)
1195 {
1196 }
1197
1198 void setDelegate(id delegate) {
1199 delegate_ = delegate;
1200 }
1201
6981ccdf
JF
1202 NSObject<ProgressDelegate> *getDelegate() const {
1203 return delegate_;
1204 }
1205
a75f53e7
JF
1206 virtual bool MediaChange(std::string media, std::string drive) {
1207 return false;
1208 }
1209
1210 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
a75f53e7
JF
1211 }
1212
1213 virtual void Fetch(pkgAcquire::ItemDesc &item) {
907a35d6 1214 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
6981ccdf 1215 [delegate_ setProgressTitle:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), [NSString stringWithUTF8String:item.ShortDesc.c_str()]]];
a75f53e7
JF
1216 }
1217
1218 virtual void Done(pkgAcquire::ItemDesc &item) {
a75f53e7
JF
1219 }
1220
1221 virtual void Fail(pkgAcquire::ItemDesc &item) {
3325a005
JF
1222 if (
1223 item.Owner->Status == pkgAcquire::Item::StatIdle ||
1224 item.Owner->Status == pkgAcquire::Item::StatDone
1225 )
1226 return;
1227
f464053e
JF
1228 std::string &error(item.Owner->ErrorText);
1229 if (error.empty())
1230 return;
1231
1232 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
1233 NSArray *fields([description componentsSeparatedByString:@" "]);
1234 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
1235
6981ccdf 1236 [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:)
f464053e
JF
1237 withObject:[NSArray arrayWithObjects:
1238 [NSString stringWithUTF8String:error.c_str()],
1239 source,
1240 nil]
238b07ce
JF
1241 waitUntilDone:YES
1242 ];
a75f53e7
JF
1243 }
1244
1245 virtual bool Pulse(pkgAcquire *Owner) {
4941f41d
JF
1246 bool value = pkgAcquireStatus::Pulse(Owner);
1247
1248 float percent(
1249 double(CurrentBytes + CurrentItems) /
1250 double(TotalBytes + TotalItems)
1251 );
1252
e057ec05 1253 [delegate_ setProgressPercent:percent];
87c76914 1254 return [delegate_ isCancelling:CurrentBytes] ? false : value;
a75f53e7
JF
1255 }
1256
1257 virtual void Start() {
87c76914 1258 [delegate_ startProgress];
a75f53e7
JF
1259 }
1260
1261 virtual void Stop() {
a75f53e7
JF
1262 }
1263};
1264/* }}} */
1265/* Progress Delegation {{{ */
1266class Progress :
1267 public OpProgress
1268{
1269 private:
e057ec05 1270 _transient id<ProgressDelegate> delegate_;
017b2b71 1271 float percent_;
a75f53e7
JF
1272
1273 protected:
1274 virtual void Update() {
6981ccdf
JF
1275 /*if (abs(Percent - percent_) > 2)
1276 //NSLog(@"%s:%s:%f", Op.c_str(), SubOp.c_str(), Percent);
017b2b71 1277 percent_ = Percent;
6981ccdf 1278 }*/
017b2b71 1279
b7ad9c68
JF
1280 /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
1281 [delegate_ setProgressPercent:(Percent / 100)];*/
a75f53e7
JF
1282 }
1283
1284 public:
1285 Progress() :
017b2b71
JF
1286 delegate_(nil),
1287 percent_(0)
a75f53e7
JF
1288 {
1289 }
1290
1291 void setDelegate(id delegate) {
1292 delegate_ = delegate;
1293 }
1294
6981ccdf
JF
1295 id getDelegate() const {
1296 return delegate_;
1297 }
1298
a75f53e7 1299 virtual void Done() {
6981ccdf 1300 //NSLog(@"DONE");
b7ad9c68 1301 //[delegate_ setProgressPercent:1];
a75f53e7
JF
1302 }
1303};
1304/* }}} */
1305
e057ec05 1306/* Database Interface {{{ */
631a0a1e
JF
1307typedef std::map< unsigned long, _H<Source> > SourceMap;
1308
e057ec05 1309@interface Database : NSObject {
6932575e
JF
1310 NSZone *zone_;
1311 apr_pool_t *pool_;
1312
a70cf746
JF
1313 unsigned era_;
1314
e057ec05 1315 pkgCacheFile cache_;
965edd52 1316 pkgDepCache::Policy *policy_;
e057ec05
JF
1317 pkgRecords *records_;
1318 pkgProblemResolver *resolver_;
1319 pkgAcquire *fetcher_;
1320 FileFd *lock_;
1321 SPtr<pkgPackageManager> manager_;
1322 pkgSourceList *list_;
b0d03ade 1323
631a0a1e 1324 SourceMap sources_;
777744da 1325 CFMutableArrayRef packages_;
686e302f 1326
238b07ce 1327 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
e057ec05
JF
1328 Status status_;
1329 Progress progress_;
7600bd69 1330
d72d91aa 1331 int cydiafd_;
e057ec05 1332 int statusfd_;
7600bd69 1333 FILE *input_;
a75f53e7
JF
1334}
1335
dbe0f181 1336+ (Database *) sharedInstance;
a70cf746 1337- (unsigned) era;
dbe0f181 1338
d72d91aa 1339- (void) _readCydia:(NSNumber *)fd;
e057ec05
JF
1340- (void) _readStatus:(NSNumber *)fd;
1341- (void) _readOutput:(NSNumber *)fd;
4941f41d 1342
7600bd69
JF
1343- (FILE *) input;
1344
e057ec05 1345- (Package *) packageWithName:(NSString *)name;
a75f53e7 1346
e057ec05 1347- (pkgCacheFile &) cache;
965edd52 1348- (pkgDepCache::Policy *) policy;
e057ec05
JF
1349- (pkgRecords *) records;
1350- (pkgProblemResolver *) resolver;
1351- (pkgAcquire &) fetcher;
ce09fc27 1352- (pkgSourceList &) list;
e057ec05 1353- (NSArray *) packages;
faf4eb4f 1354- (NSArray *) sources;
e057ec05
JF
1355- (void) reloadData;
1356
965edd52 1357- (void) configure;
6981ccdf 1358- (bool) prepare;
e057ec05 1359- (void) perform;
6981ccdf 1360- (bool) upgrade;
e057ec05
JF
1361- (void) update;
1362
6981ccdf 1363- (void) updateWithStatus:(Status &)status;
e057ec05
JF
1364
1365- (void) setDelegate:(id)delegate;
3e3977a2 1366- (Source *) getSource:(pkgCache::PkgFileIterator)file;
6981ccdf
JF
1367@end
1368/* }}} */
1369/* Delegate Helpers {{{ */
1e4922b8 1370@implementation NSObject (ProgressDelegate)
6981ccdf
JF
1371
1372- (void) _setProgressErrorPackage:(NSArray *)args {
1373 [self performSelector:@selector(setProgressError:forPackage:)
1374 withObject:[args objectAtIndex:0]
1375 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
1376 ];
1377}
1378
1379- (void) _setProgressErrorTitle:(NSArray *)args {
1380 [self performSelector:@selector(setProgressError:withTitle:)
1381 withObject:[args objectAtIndex:0]
1382 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
1383 ];
1384}
1385
1386- (void) _setProgressError:(NSString *)error withTitle:(NSString *)title {
1387 [self performSelectorOnMainThread:@selector(_setProgressErrorTitle:)
1388 withObject:[NSArray arrayWithObjects:error, title, nil]
1389 waitUntilDone:YES
1390 ];
1391}
1392
1393- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
1394 Package *package = id == nil ? nil : [[Database sharedInstance] packageWithName:id];
283fc596 1395
9b62701b
GP
1396 [self performSelector:@selector(setProgressError:withTitle:)
1397 withObject:error
1398 withObject:(package == nil ? id : [package name])
1399 ];
6981ccdf
JF
1400}
1401
a75f53e7 1402@end
e057ec05 1403/* }}} */
a75f53e7 1404
e057ec05
JF
1405/* Source Class {{{ */
1406@interface Source : NSObject {
9050015e 1407 CYString depiction_;
b8b1cfd0
JF
1408 CYString description_;
1409 CYString label_;
1410 CYString origin_;
1411 CYString support_;
a75f53e7 1412
b8b1cfd0
JF
1413 CYString uri_;
1414 CYString distribution_;
1415 CYString type_;
1416 CYString version_;
4941f41d 1417
b8b1cfd0 1418 NSString *host_;
6b8ef53e 1419 NSString *authority_;
b8b1cfd0
JF
1420
1421 CYString defaultIcon_;
a75f53e7 1422
faf4eb4f 1423 NSDictionary *record_;
e057ec05 1424 BOOL trusted_;
a75f53e7
JF
1425}
1426
b8b1cfd0 1427- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool;
a75f53e7 1428
faf4eb4f
JF
1429- (NSComparisonResult) compareByNameAndType:(Source *)source;
1430
9050015e 1431- (NSString *) depictionForPackage:(NSString *)package;
3ff1504e
JF
1432- (NSString *) supportForPackage:(NSString *)package;
1433
faf4eb4f 1434- (NSDictionary *) record;
e057ec05 1435- (BOOL) trusted;
a75f53e7 1436
e057ec05
JF
1437- (NSString *) uri;
1438- (NSString *) distribution;
1439- (NSString *) type;
faf4eb4f
JF
1440- (NSString *) key;
1441- (NSString *) host;
e057ec05 1442
faf4eb4f 1443- (NSString *) name;
e057ec05
JF
1444- (NSString *) description;
1445- (NSString *) label;
1446- (NSString *) origin;
1447- (NSString *) version;
a75f53e7 1448
e057ec05 1449- (NSString *) defaultIcon;
faf4eb4f 1450
a75f53e7 1451@end
a75f53e7 1452
e057ec05 1453@implementation Source
a75f53e7 1454
ce09fc27 1455- (void) _clear {
b8b1cfd0
JF
1456 uri_.clear();
1457 distribution_.clear();
1458 type_.clear();
ce09fc27 1459
b8b1cfd0
JF
1460 description_.clear();
1461 label_.clear();
1462 origin_.clear();
9050015e 1463 depiction_.clear();
b8b1cfd0
JF
1464 support_.clear();
1465 version_.clear();
1466 defaultIcon_.clear();
1467
1468 if (record_ != nil) {
1469 [record_ release];
1470 record_ = nil;
1471 }
1472
1473 if (host_ != nil) {
1474 [host_ release];
1475 host_ = nil;
1476 }
419a9efd
JF
1477
1478 if (authority_ != nil) {
1479 [authority_ release];
1480 authority_ = nil;
1481 }
ce09fc27 1482}
a75f53e7 1483
ce09fc27 1484- (void) dealloc {
a591888a 1485 // XXX: this is a very inefficient way to call these deconstructors
ce09fc27 1486 [self _clear];
e057ec05 1487 [super dealloc];
a75f53e7
JF
1488}
1489
ad554f10
JF
1490+ (NSArray *) _attributeKeys {
1491 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1492}
1493
1494- (NSArray *) attributeKeys {
1495 return [[self class] _attributeKeys];
1496}
1497
1498+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1499 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1500}
1501
b8b1cfd0 1502- (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
ce09fc27 1503 [self _clear];
a75f53e7 1504
ce09fc27
JF
1505 trusted_ = index->IsTrusted();
1506
b8b1cfd0
JF
1507 uri_.set(pool, index->GetURI());
1508 distribution_.set(pool, index->GetDist());
1509 type_.set(pool, index->GetType());
ce09fc27
JF
1510
1511 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1512 if (dindex != NULL) {
f30eaf83
JF
1513 FileFd fd;
1514 if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly))
1515 _error->Discard();
1516 else {
1517 pkgTagFile tags(&fd);
b8b1cfd0 1518
f30eaf83
JF
1519 pkgTagSection section;
1520 tags.Step(section);
ce09fc27 1521
f30eaf83
JF
1522 struct {
1523 const char *name_;
1524 CYString *value_;
1525 } names[] = {
1526 {"default-icon", &defaultIcon_},
9050015e 1527 {"depiction", &depiction_},
f30eaf83
JF
1528 {"description", &description_},
1529 {"label", &label_},
1530 {"origin", &origin_},
1531 {"support", &support_},
1532 {"version", &version_},
1533 };
b8b1cfd0 1534
f30eaf83
JF
1535 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
1536 const char *start, *end;
1537
1538 if (section.Find(names[i].name_, start, end)) {
1539 CYString &value(*names[i].value_);
1540 value.set(pool, start, end - start);
1541 }
b8b1cfd0 1542 }
e057ec05 1543 }
ce09fc27 1544 }
faf4eb4f 1545
ce09fc27
JF
1546 record_ = [Sources_ objectForKey:[self key]];
1547 if (record_ != nil)
1548 record_ = [record_ retain];
b8b1cfd0 1549
419a9efd
JF
1550 NSURL *url([NSURL URLWithString:uri_]);
1551
1552 host_ = [url host];
1553 if (host_ != nil)
1554 host_ = [[host_ lowercaseString] retain];
1555
1556 if (host_ != nil)
12b7669a 1557 authority_ = host_;
419a9efd
JF
1558 else
1559 authority_ = [url path];
12b7669a
JF
1560
1561 if (authority_ != nil)
1562 authority_ = [authority_ retain];
ce09fc27
JF
1563}
1564
b8b1cfd0 1565- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
ce09fc27 1566 if ((self = [super init]) != nil) {
b8b1cfd0 1567 [self setMetaIndex:index inPool:pool];
e057ec05 1568 } return self;
4941f41d 1569}
a75f53e7 1570
faf4eb4f
JF
1571- (NSComparisonResult) compareByNameAndType:(Source *)source {
1572 NSDictionary *lhr = [self record];
1573 NSDictionary *rhr = [source record];
1574
1575 if (lhr != rhr)
1576 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1577
1578 NSString *lhs = [self name];
1579 NSString *rhs = [source name];
1580
1581 if ([lhs length] != 0 && [rhs length] != 0) {
1582 unichar lhc = [lhs characterAtIndex:0];
1583 unichar rhc = [rhs characterAtIndex:0];
1584
1585 if (isalpha(lhc) && !isalpha(rhc))
1586 return NSOrderedAscending;
1587 else if (!isalpha(lhc) && isalpha(rhc))
1588 return NSOrderedDescending;
1589 }
1590
9c4e0cbe 1591 return [lhs compare:rhs options:LaxCompareOptions_];
faf4eb4f
JF
1592}
1593
9050015e 1594- (NSString *) depictionForPackage:(NSString *)package {
cbc40330 1595 return depiction_.empty() ? nil : [static_cast<id>(depiction_) stringByReplacingOccurrencesOfString:@"*" withString:package];
9050015e
JF
1596}
1597
3ff1504e 1598- (NSString *) supportForPackage:(NSString *)package {
cbc40330 1599 return support_.empty() ? nil : [static_cast<id>(support_) stringByReplacingOccurrencesOfString:@"*" withString:package];
3ff1504e
JF
1600}
1601
faf4eb4f
JF
1602- (NSDictionary *) record {
1603 return record_;
1604}
1605
e057ec05
JF
1606- (BOOL) trusted {
1607 return trusted_;
1608}
2d28b35a 1609
e057ec05
JF
1610- (NSString *) uri {
1611 return uri_;
1612}
1cb11c5f 1613
e057ec05
JF
1614- (NSString *) distribution {
1615 return distribution_;
1616}
a75f53e7 1617
e057ec05
JF
1618- (NSString *) type {
1619 return type_;
a75f53e7
JF
1620}
1621
faf4eb4f 1622- (NSString *) key {
b8b1cfd0 1623 return [NSString stringWithFormat:@"%@:%@:%@", (NSString *) type_, (NSString *) uri_, (NSString *) distribution_];
faf4eb4f
JF
1624}
1625
1626- (NSString *) host {
b8b1cfd0 1627 return host_;
faf4eb4f
JF
1628}
1629
1630- (NSString *) name {
419a9efd 1631 return origin_.empty() ? authority_ : origin_;
faf4eb4f
JF
1632}
1633
e057ec05
JF
1634- (NSString *) description {
1635 return description_;
1636}
686e302f 1637
e057ec05 1638- (NSString *) label {
419a9efd 1639 return label_.empty() ? authority_ : label_;
e057ec05 1640}
2d28b35a 1641
e057ec05
JF
1642- (NSString *) origin {
1643 return origin_;
1644}
2d28b35a 1645
e057ec05
JF
1646- (NSString *) version {
1647 return version_;
1648}
4941f41d 1649
e057ec05
JF
1650- (NSString *) defaultIcon {
1651 return defaultIcon_;
1652}
4941f41d 1653
2a987aa5
JF
1654@end
1655/* }}} */
1656/* Relationship Class {{{ */
1657@interface Relationship : NSObject {
1658 NSString *type_;
1659 NSString *id_;
1660}
1661
1662- (NSString *) type;
1663- (NSString *) id;
1664- (NSString *) name;
1665
1666@end
1667
1668@implementation Relationship
1669
1670- (void) dealloc {
1671 [type_ release];
1672 [id_ release];
1673 [super dealloc];
1674}
1675
1676- (NSString *) type {
1677 return type_;
1678}
1679
1680- (NSString *) id {
1681 return id_;
1682}
1683
1684- (NSString *) name {
1685 _assert(false);
1686 return nil;
1687}
1688
a75f53e7 1689@end
686e302f 1690/* }}} */
e057ec05 1691/* Package Class {{{ */
e057ec05 1692@interface Package : NSObject {
a70cf746 1693 unsigned era_;
631a0a1e 1694 apr_pool_t *pool_;
a70cf746 1695
3e3977a2 1696 pkgCache::VerIterator version_;
e057ec05
JF
1697 pkgCache::PkgIterator iterator_;
1698 _transient Database *database_;
e057ec05 1699 pkgCache::VerFileIterator file_;
3d37fc0d 1700
e057ec05 1701 Source *source_;
631a0a1e 1702 bool parsed_;
a75f53e7 1703
6932575e 1704 CYString section_;
a591888a 1705 _transient NSString *section$_;
a70cf746 1706 bool essential_;
5ec44e34 1707 bool obsolete_;
f159ecd4 1708
e057ec05 1709 NSString *latest_;
b4c4fac4 1710 CYString installed_;
a75f53e7 1711
9ee296df 1712 CYString id_;
6932575e
JF
1713 CYString name_;
1714 CYString tagline_;
1715 CYString icon_;
1716 CYString depiction_;
1717 CYString homepage_;
1718
1719 CYString sponsor_;
1720 Address *sponsor$_;
1721
1722 CYString author_;
1723 Address *author$_;
1724
419a9efd 1725 CYString bugs_;
6932575e 1726 CYString support_;
fbe40361 1727 NSMutableArray *tags_;
faf4eb4f 1728 NSString *role_;
2a987aa5 1729
6932575e 1730 NSMutableDictionary *metadata_;
43b742af
JF
1731 _transient NSDate *firstSeen_;
1732 _transient NSDate *lastSeen_;
1733 bool subscribed_;
686e302f
JF
1734}
1735
3e3977a2 1736- (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
6932575e 1737+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
686e302f 1738
2a987aa5 1739- (pkgCache::PkgIterator) iterator;
631a0a1e 1740- (void) parse;
2a987aa5 1741
e057ec05 1742- (NSString *) section;
6b4b3bee
JF
1743- (NSString *) simpleSection;
1744
6932575e
JF
1745- (NSString *) longSection;
1746- (NSString *) shortSection;
1747
ce09fc27
JF
1748- (NSString *) uri;
1749
e057ec05
JF
1750- (Address *) maintainer;
1751- (size_t) size;
66abcbb0
JF
1752- (NSString *) longDescription;
1753- (NSString *) shortDescription;
3bddda52 1754- (unichar) index;
a75f53e7 1755
f159ecd4 1756- (NSMutableDictionary *) metadata;
e057ec05 1757- (NSDate *) seen;
f159ecd4
JF
1758- (BOOL) subscribed;
1759- (BOOL) ignored;
63a1e4b8 1760
e057ec05
JF
1761- (NSString *) latest;
1762- (NSString *) installed;
b4c4fac4 1763- (BOOL) uninstalled;
965edd52
JF
1764
1765- (BOOL) valid;
238b07ce 1766- (BOOL) upgradableAndEssential:(BOOL)essential;
e057ec05
JF
1767- (BOOL) essential;
1768- (BOOL) broken;
853d14d3 1769- (BOOL) unfiltered;
fa7bb92f 1770- (BOOL) visible;
686e302f 1771
3319715b
JF
1772- (BOOL) half;
1773- (BOOL) halfConfigured;
1774- (BOOL) halfInstalled;
1775- (BOOL) hasMode;
1776- (NSString *) mode;
1777
e057ec05
JF
1778- (NSString *) id;
1779- (NSString *) name;
dbe0f181 1780- (UIImage *) icon;
ad554f10 1781- (NSString *) homepage;
0235116c 1782- (NSString *) depiction;
d72d91aa 1783- (Address *) author;
686e302f 1784
3ff1504e
JF
1785- (NSString *) support;
1786
f464053e 1787- (NSArray *) files;
f464053e
JF
1788- (NSArray *) warnings;
1789- (NSArray *) applications;
2a987aa5 1790
e057ec05 1791- (Source *) source;
faf4eb4f 1792- (NSString *) role;
686e302f 1793
e057ec05 1794- (BOOL) matches:(NSString *)text;
686e302f 1795
faf4eb4f 1796- (bool) hasSupportingRole;
fa7bb92f 1797- (BOOL) hasTag:(NSString *)tag;
cb9c2100 1798- (NSString *) primaryPurpose;
dbe0f181 1799- (NSArray *) purposes;
d8d9a65c 1800- (bool) isCommercial;
fa7bb92f 1801
dd9390c5
JF
1802- (CYString &) cyname;
1803
6932575e 1804- (uint32_t) compareBySection:(NSArray *)sections;
f159ecd4
JF
1805
1806- (uint32_t) compareForChanges;
686e302f 1807
e057ec05
JF
1808- (void) install;
1809- (void) remove;
7e986211 1810
7cf54836 1811- (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
5ec44e34 1812- (bool) isUnfilteredAndSelectedForBy:(NSString *)search;
1c220bde 1813- (bool) isInstalledAndUnfiltered:(NSNumber *)number;
6981ccdf 1814- (bool) isVisibleInSection:(NSString *)section;
7cf54836 1815- (bool) isVisibleInSource:(Source *)source;
686e302f 1816
e057ec05 1817@end
686e302f 1818
6932575e
JF
1819uint32_t PackageChangesRadix(Package *self, void *) {
1820 union {
1821 uint32_t key;
1822
1823 struct {
1824 uint32_t timestamp : 30;
1825 uint32_t ignored : 1;
1826 uint32_t upgradable : 1;
1827 } bits;
1828 } value;
1829
1830 bool upgradable([self upgradableAndEssential:YES]);
1831 value.bits.upgradable = upgradable ? 1 : 0;
1832
1833 if (upgradable) {
1834 value.bits.timestamp = 0;
1835 value.bits.ignored = [self ignored] ? 0 : 1;
1836 value.bits.upgradable = 1;
1837 } else {
1838 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1839 value.bits.ignored = 0;
1840 value.bits.upgradable = 0;
1841 }
1842
1843 return _not(uint32_t) - value.key;
1844}
1845
dd9390c5
JF
1846_finline static void Stifle(uint8_t &value) {
1847}
43b742af 1848
dd9390c5
JF
1849uint32_t PackagePrefixRadix(Package *self, void *context) {
1850 size_t offset(reinterpret_cast<size_t>(context));
1851 CYString &name([self cyname]);
43b742af 1852
dd9390c5
JF
1853 size_t size(name.size());
1854 if (size == 0)
1855 return 0;
1856 char *text(name.data());
43b742af 1857
dd9390c5
JF
1858 size_t zeros;
1859 if (!isdigit(text[0]))
1860 zeros = 0;
1861 else {
1862 size_t digits(1);
1863 while (size != digits && isdigit(text[digits]))
1864 if (++digits == 4)
1865 break;
1866 zeros = 4 - digits;
1867 }
43b742af 1868
dd9390c5
JF
1869 uint8_t data[4];
1870
1871 // 0.607997
1872
1873 if (offset == 0 && zeros != 0) {
1874 memset(data, '0', zeros);
1875 memcpy(data + zeros, text, 4 - zeros);
1876 } else {
1877 /* XXX: there's some danger here if you request a non-zero offset < 4 and it gets zero padded */
1878 if (size <= offset - zeros)
1879 return 0;
1880
1881 text += offset - zeros;
1882 size -= offset - zeros;
1883
1884 if (size >= 4)
1885 memcpy(data, text, 4);
1886 else {
1887 memcpy(data, text, size);
1888 memset(data + size, 0, 4 - size);
1889 }
1890
1891 for (size_t i(0); i != 4; ++i)
1892 if (isalpha(data[i]))
440a8f9a 1893 data[i] |= 0x20;
dd9390c5
JF
1894 }
1895
1896 if (offset == 0)
440a8f9a
JF
1897 if (data[0] == '@')
1898 data[0] = 0x7f;
1899 else
1900 data[0] = (data[0] & 0x1f) | "\x80\x00\xc0\x40"[data[0] >> 6];
dd9390c5
JF
1901
1902 /* XXX: ntohl may be more honest */
1903 return OSSwapInt32(*reinterpret_cast<uint32_t *>(data));
1904}
1905
1906CYString &(*PackageName)(Package *self, SEL sel);
1907
1908CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) {
1909 _profile(PackageNameCompare)
1910 CYString &lhi(PackageName(lhs, @selector(cyname)));
1911 CYString &rhi(PackageName(rhs, @selector(cyname)));
1912 CFStringRef lhn(lhi), rhn(rhi);
43b742af 1913
08383255
JF
1914 if (lhn == NULL)
1915 return rhn == NULL ? NSOrderedSame : NSOrderedAscending;
1916 else if (rhn == NULL)
1917 return NSOrderedDescending;
1918
43b742af 1919 _profile(PackageNameCompare$NumbersLast)
dd9390c5 1920 if (!lhi.empty() && !rhi.empty()) {
43b742af
JF
1921 UniChar lhc(CFStringGetCharacterAtIndex(lhn, 0));
1922 UniChar rhc(CFStringGetCharacterAtIndex(rhn, 0));
1923 bool lha(CFUniCharIsMemberOf(lhc, kCFUniCharLetterCharacterSet));
1924 if (lha != CFUniCharIsMemberOf(rhc, kCFUniCharLetterCharacterSet))
1925 return lha ? NSOrderedAscending : NSOrderedDescending;
1926 }
1927 _end
1928
dd9390c5
JF
1929 CFIndex length = CFStringGetLength(lhn);
1930
43b742af
JF
1931 _profile(PackageNameCompare$Compare)
1932 return CFStringCompareWithOptionsAndLocale(lhn, rhn, CFRangeMake(0, length), LaxCompareFlags_, Locale_);
1933 _end
1934 _end
1935}
1936
66abcbb0
JF
1937CFComparisonResult PackageNameCompare_(Package **lhs, Package **rhs, void *context) {
1938 return PackageNameCompare(*lhs, *rhs, context);
43b742af
JF
1939}
1940
1941struct PackageNameOrdering :
1942 std::binary_function<Package *, Package *, bool>
1943{
1944 _finline bool operator ()(Package *lhs, Package *rhs) const {
1945 return PackageNameCompare(lhs, rhs, NULL) == NSOrderedAscending;
1946 }
1947};
1948
e057ec05 1949@implementation Package
686e302f 1950
dd9390c5
JF
1951- (NSString *) description {
1952 return [NSString stringWithFormat:@"<Package:%@>", static_cast<NSString *>(name_)];
1953}
1954
e057ec05 1955- (void) dealloc {
3d37fc0d
JF
1956 if (source_ != nil)
1957 [source_ release];
f159ecd4 1958
3e3977a2
JF
1959 if (latest_ != nil)
1960 [latest_ release];
686e302f 1961
6932575e
JF
1962 if (sponsor$_ != nil)
1963 [sponsor$_ release];
1964 if (author$_ != nil)
1965 [author$_ release];
fa7bb92f
JF
1966 if (tags_ != nil)
1967 [tags_ release];
faf4eb4f
JF
1968 if (role_ != nil)
1969 [role_ release];
63a1e4b8 1970
6932575e
JF
1971 if (metadata_ != nil)
1972 [metadata_ release];
2a987aa5 1973
e057ec05 1974 [super dealloc];
686e302f
JF
1975}
1976
d8d9a65c
JF
1977+ (NSString *) webScriptNameForSelector:(SEL)selector {
1978 if (selector == @selector(hasTag:))
1979 return @"hasTag";
1980 else
1981 return nil;
1982}
1983
1984+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
1985 return [self webScriptNameForSelector:selector] == nil;
1986}
1987
ad554f10 1988+ (NSArray *) _attributeKeys {
66abcbb0 1989 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"longDescription", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"longSection", @"maintainer", @"mode", @"name", @"purposes", @"section", @"shortDescription", @"shortSection", @"simpleSection", @"size", @"source", @"sponsor", @"support", @"warnings", nil];
ad554f10
JF
1990}
1991
1992- (NSArray *) attributeKeys {
1993 return [[self class] _attributeKeys];
1994}
1995
1996+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1997 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1998}
1999
631a0a1e
JF
2000- (void) parse {
2001 if (parsed_)
2002 return;
2003 parsed_ = true;
2004 if (file_.end())
2005 return;
2006
2007 _profile(Package$parse)
2008 pkgRecords::Parser *parser;
2009
2010 _profile(Package$parse$Lookup)
2011 parser = &[database_ records]->Lookup(file_);
2012 _end
2013
2014 CYString website;
2015
2016 _profile(Package$parse$Find)
2017 struct {
2018 const char *name_;
2019 CYString *value_;
2020 } names[] = {
2021 {"icon", &icon_},
2022 {"depiction", &depiction_},
2023 {"homepage", &homepage_},
2024 {"website", &website},
419a9efd 2025 {"bugs", &bugs_},
631a0a1e
JF
2026 {"support", &support_},
2027 {"sponsor", &sponsor_},
2028 {"author", &author_},
2029 };
2030
2031 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
2032 const char *start, *end;
2033
2034 if (parser->Find(names[i].name_, start, end)) {
2035 CYString &value(*names[i].value_);
2036 _profile(Package$parse$Value)
2037 value.set(pool_, start, end - start);
2038 _end
2039 }
2040 }
2041 _end
2042
2043 _profile(Package$parse$Tagline)
2044 const char *start, *end;
fbe40361 2045 if (parser->ShortDesc(start, end)) {
631a0a1e
JF
2046 const char *stop(reinterpret_cast<const char *>(memchr(start, '\n', end - start)));
2047 if (stop == NULL)
2048 stop = end;
2049 while (stop != start && stop[-1] == '\r')
2050 --stop;
2051 tagline_.set(pool_, start, stop - start);
2052 }
2053 _end
2054
2055 _profile(Package$parse$Retain)
7affe45b 2056 if (homepage_.empty())
631a0a1e
JF
2057 homepage_ = website;
2058 if (homepage_ == depiction_)
2059 homepage_.clear();
2060 _end
2061 _end
2062}
2063
3e3977a2 2064- (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
a70cf746 2065 if ((self = [super init]) != nil) {
3e3977a2 2066 _profile(Package$initWithVersion)
a70cf746 2067 era_ = [database era];
631a0a1e 2068 pool_ = pool;
a70cf746 2069
3e3977a2 2070 version_ = version;
9c8417b0
JF
2071
2072 _profile(Package$initWithVersion$ParentPkg)
2073 iterator_ = version.ParentPkg();
2074 _end
2075
e057ec05 2076 database_ = database;
686e302f 2077
3e3977a2 2078 _profile(Package$initWithVersion$Latest)
5bb2842a
JF
2079 const char *latest(StripVersion_(version_.VerStr()));
2080 latest_ = (NSString *) CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(latest), strlen(latest), kCFStringEncodingASCII, NO);
3bddda52 2081 _end
e2a207dd 2082
631a0a1e
JF
2083 pkgCache::VerIterator current;
2084 _profile(Package$initWithVersion$Versions)
2085 current = iterator_.CurrentVer();
2086 if (!current.end())
b4c4fac4 2087 installed_.set(pool_, StripVersion_(current.VerStr()));
7e986211 2088
631a0a1e
JF
2089 if (!version_.end())
2090 file_ = version_.FileList();
2091 else {
2092 pkgCache &cache([database_ cache]);
2093 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
2094 }
2095 _end
3bddda52 2096
3e3977a2 2097 _profile(Package$initWithVersion$Name)
631a0a1e 2098 id_.set(pool_, iterator_.Name());
fbe40361 2099 name_.set(pool, iterator_.Display());
3bddda52
JF
2100 _end
2101
9c8417b0 2102 _profile(Package$initWithVersion$lowercaseString)
c400e87f
JF
2103 char *data(id_.data());
2104 for (size_t i(0), e(id_.size()); i != e; ++i)
2105 // XXX: do not use tolower() as this is not locale-specific? :(
2106 data[i] |= 0x20;
2107 _end
2108
3e3977a2 2109 _profile(Package$initWithVersion$Tags)
fbe40361
JF
2110 pkgCache::TagIterator tag(iterator_.TagList());
2111 if (!tag.end()) {
2112 tags_ = [[NSMutableArray alloc] initWithCapacity:8];
2113 do {
2114 const char *name(tag.Name());
a591888a 2115 [tags_ addObject:[(NSString *)CYStringCreate(name) autorelease]];
bd8e54e1 2116 if (role_ == nil && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/)
2a0a85d0 2117 role_ = (NSString *) CYStringCreate(name + 6);
fbe40361
JF
2118 ++tag;
2119 } while (!tag.end());
2120 }
3bddda52 2121 _end
faf4eb4f 2122
6b92acab 2123 bool changed(false);
56e10908 2124
3e3977a2 2125 _profile(Package$initWithVersion$Metadata)
c400e87f 2126 metadata_ = [Packages_ objectForKey:id_];
43b742af 2127
6932575e 2128 if (metadata_ == nil) {
98fb9119 2129 firstSeen_ = now_;
43b742af 2130
6932575e 2131 metadata_ = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
43b742af
JF
2132 firstSeen_, @"FirstSeen",
2133 latest_, @"LastVersion",
3bddda52 2134 nil] mutableCopy];
6b92acab 2135
3bddda52
JF
2136 changed = true;
2137 } else {
43b742af
JF
2138 firstSeen_ = [metadata_ objectForKey:@"FirstSeen"];
2139 lastSeen_ = [metadata_ objectForKey:@"LastSeen"];
2140
2141 if (NSNumber *subscribed = [metadata_ objectForKey:@"IsSubscribed"])
2142 subscribed_ = [subscribed boolValue];
2143
6932575e 2144 NSString *version([metadata_ objectForKey:@"LastVersion"]);
3bddda52 2145
43b742af
JF
2146 if (firstSeen_ == nil) {
2147 firstSeen_ = lastSeen_ == nil ? now_ : lastSeen_;
2148 [metadata_ setObject:firstSeen_ forKey:@"FirstSeen"];
6b92acab
JF
2149 changed = true;
2150 }
6b92acab 2151
3e3977a2
JF
2152 if (version == nil) {
2153 [metadata_ setObject:latest_ forKey:@"LastVersion"];
2154 changed = true;
5ec44e34 2155 } else if (![version isEqualToString:latest_]) {
3e3977a2 2156 [metadata_ setObject:latest_ forKey:@"LastVersion"];
43b742af
JF
2157 lastSeen_ = now_;
2158 [metadata_ setObject:lastSeen_ forKey:@"LastSeen"];
3e3977a2 2159 changed = true;
5ec44e34 2160 }
3bddda52
JF
2161 }
2162
6932575e
JF
2163 metadata_ = [metadata_ retain];
2164
3bddda52 2165 if (changed) {
c400e87f 2166 [Packages_ setObject:metadata_ forKey:id_];
3bddda52
JF
2167 Changed_ = true;
2168 }
2169 _end
a70cf746 2170
3e3977a2 2171 _profile(Package$initWithVersion$Section)
631a0a1e 2172 section_.set(pool_, iterator_.Section());
6932575e 2173 _end
a70cf746 2174
9c8417b0
JF
2175 _profile(Package$initWithVersion$hasTag)
2176 obsolete_ = [self hasTag:@"cydia::obsolete"];
2177 essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
2178 _end
f3d8816a 2179 _end } return self;
a75f53e7
JF
2180}
2181
6932575e 2182+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
bb9edf8b
JF
2183 pkgCache::VerIterator version;
2184
2185 _profile(Package$packageWithIterator$GetCandidateVer)
2186 version = [database policy]->GetCandidateVer(iterator);
2187 _end
2188
3e3977a2
JF
2189 if (version.end())
2190 return nil;
bb9edf8b 2191
965edd52 2192 return [[[Package alloc]
3e3977a2 2193 initWithVersion:version
6932575e
JF
2194 withZone:zone
2195 inPool:pool
965edd52
JF
2196 database:database
2197 ] autorelease];
f3d8816a 2198}
a75f53e7 2199
2a987aa5
JF
2200- (pkgCache::PkgIterator) iterator {
2201 return iterator_;
2202}
2203
e057ec05 2204- (NSString *) section {
6932575e
JF
2205 if (section$_ == nil) {
2206 if (section_.empty())
2207 return nil;
2208
1c220bde
JF
2209 _profile(Package$section)
2210 std::replace(section_.data(), section_.data() + section_.size(), '_', ' ');
2211 NSString *name(section_);
6932575e 2212
1c220bde
JF
2213 lookup:
2214 if (NSDictionary *value = [SectionMap_ objectForKey:name])
2215 if (NSString *rename = [value objectForKey:@"Rename"]) {
2216 name = rename;
2217 goto lookup;
2218 }
6932575e 2219
1c220bde
JF
2220 section$_ = name;
2221 _end
6932575e 2222 } return section$_;
a75f53e7
JF
2223}
2224
6b4b3bee
JF
2225- (NSString *) simpleSection {
2226 if (NSString *section = [self section])
2227 return Simplify(section);
2228 else
2229 return nil;
ce09fc27 2230}
6b4b3bee 2231
6932575e 2232- (NSString *) longSection {
f30eaf83 2233 return LocalizeSection([self section]);
6932575e
JF
2234}
2235
2236- (NSString *) shortSection {
2237 return [[NSBundle mainBundle] localizedStringForKey:[self simpleSection] value:nil table:@"Sections"];
2238}
2239
ce09fc27
JF
2240- (NSString *) uri {
2241 return nil;
2242#if 0
2243 pkgIndexFile *index;
2244 pkgCache::PkgFileIterator file(file_.File());
2245 if (![database_ list].FindIndex(file, index))
2246 return nil;
2247 return [NSString stringWithUTF8String:iterator_->Path];
2248 //return [NSString stringWithUTF8String:file.Site()];
2249 //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
2250#endif
6b4b3bee
JF
2251}
2252
e057ec05 2253- (Address *) maintainer {
965edd52
JF
2254 if (file_.end())
2255 return nil;
e057ec05 2256 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
0a7e5478
JF
2257 const std::string &maintainer(parser->Maintainer());
2258 return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
b6ffa083
JF
2259}
2260
e057ec05 2261- (size_t) size {
965edd52 2262 return version_.end() ? 0 : version_->InstalledSize;
a75f53e7
JF
2263}
2264
66abcbb0 2265- (NSString *) longDescription {
f3d8816a
JF
2266@synchronized (database_) {
2267 if ([database_ era] != era_ || file_.end())
965edd52 2268 return nil;
f3d8816a 2269
e057ec05 2270 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
2a987aa5 2271 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
b6ffa083 2272
e057ec05
JF
2273 NSArray *lines = [description componentsSeparatedByString:@"\n"];
2274 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
2275 if ([lines count] < 2)
2276 return nil;
2d28b35a 2277
e057ec05 2278 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
9fdd37d0 2279 for (size_t i(1), e([lines count]); i != e; ++i) {
e057ec05
JF
2280 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
2281 [trimmed addObject:trim];
2282 }
2d28b35a 2283
e057ec05 2284 return [trimmed componentsJoinedByString:@"\n"];
f3d8816a 2285} }
a75f53e7 2286
66abcbb0
JF
2287- (NSString *) shortDescription {
2288 return tagline_;
2289}
2290
3bddda52
JF
2291- (unichar) index {
2292 _profile(Package$index)
43b742af
JF
2293 CFStringRef name((CFStringRef) [self name]);
2294 if (CFStringGetLength(name) == 0)
3bddda52 2295 return '#';
43b742af
JF
2296 UniChar character(CFStringGetCharacterAtIndex(name, 0));
2297 if (!CFUniCharIsMemberOf(character, kCFUniCharLetterCharacterSet))
3bddda52 2298 return '#';
c46df204 2299 return toupper(character);
3bddda52 2300 _end
e057ec05 2301}
2d28b35a 2302
f159ecd4 2303- (NSMutableDictionary *) metadata {
6932575e 2304 return metadata_;
f159ecd4
JF
2305}
2306
e057ec05 2307- (NSDate *) seen {
43b742af
JF
2308 if (subscribed_ && lastSeen_ != nil)
2309 return lastSeen_;
2310 return firstSeen_;
2d28b35a
JF
2311}
2312
f159ecd4 2313- (BOOL) subscribed {
43b742af 2314 return subscribed_;
f159ecd4
JF
2315}
2316
2317- (BOOL) ignored {
2318 NSDictionary *metadata([self metadata]);
2319 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
2320 return [ignored boolValue];
2321 else
2322 return false;
2323}
2324
e057ec05
JF
2325- (NSString *) latest {
2326 return latest_;
b6ffa083
JF
2327}
2328
e057ec05
JF
2329- (NSString *) installed {
2330 return installed_;
2d28b35a
JF
2331}
2332
b4c4fac4
JF
2333- (BOOL) uninstalled {
2334 return installed_.empty();
2335}
2336
965edd52
JF
2337- (BOOL) valid {
2338 return !version_.end();
2339}
2340
238b07ce 2341- (BOOL) upgradableAndEssential:(BOOL)essential {
43b742af
JF
2342 _profile(Package$upgradableAndEssential)
2343 pkgCache::VerIterator current(iterator_.CurrentVer());
2344 if (current.end())
1c220bde 2345 return essential && essential_;
43b742af 2346 else
1c220bde 2347 return !version_.end() && version_ != current;
43b742af 2348 _end
e057ec05 2349}
2d28b35a 2350
e057ec05 2351- (BOOL) essential {
a70cf746 2352 return essential_;
2d28b35a
JF
2353}
2354
e057ec05 2355- (BOOL) broken {
3319715b
JF
2356 return [database_ cache][iterator_].InstBroken();
2357}
2358
853d14d3 2359- (BOOL) unfiltered {
9c8417b0
JF
2360 _profile(Package$unfiltered$obsolete)
2361 if (obsolete_)
2362 return false;
2363 _end
2364
2365 _profile(Package$unfiltered$hasSupportingRole)
2366 if (![self hasSupportingRole])
2367 return false;
2368 _end
2369
1c220bde
JF
2370 return true;
2371}
9c8417b0 2372
1c220bde
JF
2373- (BOOL) visible {
2374 if (![self unfiltered])
2375 return false;
2376
2377 NSString *section([self section]);
9c8417b0 2378
1c220bde 2379 _profile(Package$visible$isSectionVisible)
9c8417b0
JF
2380 if (section != nil && !isSectionVisible(section))
2381 return false;
2382 _end
2383
2384 return true;
853d14d3
JF
2385}
2386
3319715b 2387- (BOOL) half {
43b742af 2388 unsigned char current(iterator_->CurrentState);
3319715b
JF
2389 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
2390}
2391
2392- (BOOL) halfConfigured {
2393 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
2394}
2395
2396- (BOOL) halfInstalled {
2397 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
2398}
2399
2400- (BOOL) hasMode {
2401 pkgDepCache::StateCache &state([database_ cache][iterator_]);
2402 return state.Mode != pkgDepCache::ModeKeep;
2403}
2404
2405- (NSString *) mode {
2406 pkgDepCache::StateCache &state([database_ cache][iterator_]);
2407
2408 switch (state.Mode) {
2409 case pkgDepCache::ModeDelete:
2410 if ((state.iFlags & pkgDepCache::Purge) != 0)
6932575e 2411 return @"PURGE";
3319715b 2412 else
6932575e 2413 return @"REMOVE";
3319715b 2414 case pkgDepCache::ModeKeep:
3ff1504e 2415 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
6932575e 2416 return @"REINSTALL";
3ff1504e
JF
2417 /*else if ((state.iFlags & pkgDepCache::AutoKept) != 0)
2418 return nil;*/
3319715b
JF
2419 else
2420 return nil;
3319715b 2421 case pkgDepCache::ModeInstall:
3ff1504e 2422 /*if ((state.iFlags & pkgDepCache::ReInstall) != 0)
6932575e 2423 return @"REINSTALL";
3ff1504e 2424 else*/ switch (state.Status) {
3319715b 2425 case -1:
6932575e 2426 return @"DOWNGRADE";
3319715b 2427 case 0:
6932575e 2428 return @"INSTALL";
3319715b 2429 case 1:
6932575e 2430 return @"UPGRADE";
3319715b 2431 case 2:
6932575e 2432 return @"NEW_INSTALL";
6981ccdf 2433 _nodefault
3319715b 2434 }
6981ccdf 2435 _nodefault
3319715b 2436 }
b6ffa083
JF
2437}
2438
e057ec05
JF
2439- (NSString *) id {
2440 return id_;
b6ffa083
JF
2441}
2442
e057ec05 2443- (NSString *) name {
9ee296df 2444 return name_.empty() ? id_ : name_;
e057ec05 2445}
b6ffa083 2446
dbe0f181 2447- (UIImage *) icon {
6b4b3bee 2448 NSString *section = [self simpleSection];
dbe0f181
JF
2449
2450 UIImage *icon(nil);
dd9390c5 2451 if (!icon_.empty())
cbc40330 2452 if ([static_cast<id>(icon_) hasPrefix:@"file:///"])
69a8c80e 2453 // XXX: correct escaping
cbc40330 2454 icon = [UIImage imageAtPath:[static_cast<id>(icon_) substringFromIndex:7]];
dbe0f181
JF
2455 if (icon == nil) if (section != nil)
2456 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
6e673d99
JF
2457 if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
2458 if ([dicon hasPrefix:@"file:///"])
69a8c80e 2459 // XXX: correct escaping
6e673d99 2460 icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
dbe0f181
JF
2461 if (icon == nil)
2462 icon = [UIImage applicationImageNamed:@"unknown.png"];
2463 return icon;
e057ec05 2464}
b6ffa083 2465
ad554f10 2466- (NSString *) homepage {
ff47b800 2467 return homepage_;
b6ffa083
JF
2468}
2469
0235116c 2470- (NSString *) depiction {
9050015e 2471 return !depiction_.empty() ? depiction_ : [[self source] depictionForPackage:id_];
0235116c
JF
2472}
2473
81ab76dc 2474- (Address *) sponsor {
6932575e
JF
2475 if (sponsor$_ == nil) {
2476 if (sponsor_.empty())
2477 return nil;
2478 sponsor$_ = [[Address addressWithString:sponsor_] retain];
2479 } return sponsor$_;
81ab76dc
JF
2480}
2481
d72d91aa 2482- (Address *) author {
6932575e
JF
2483 if (author$_ == nil) {
2484 if (author_.empty())
2485 return nil;
2486 author$_ = [[Address addressWithString:author_] retain];
2487 } return author$_;
d72d91aa
JF
2488}
2489
3ff1504e 2490- (NSString *) support {
419a9efd 2491 return !bugs_.empty() ? bugs_ : [[self source] supportForPackage:id_];
3ff1504e
JF
2492}
2493
f464053e 2494- (NSArray *) files {
9ee296df 2495 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", static_cast<NSString *>(id_)];
f464053e
JF
2496 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
2497
2498 std::ifstream fin;
2499 fin.open([path UTF8String]);
2500 if (!fin.is_open())
2501 return nil;
2502
2503 std::string line;
2504 while (std::getline(fin, line))
2505 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
2506
2507 return files;
2508}
2509
f464053e
JF
2510- (NSArray *) warnings {
2511 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
2512 const char *name(iterator_.Name());
2513
2514 size_t length(strlen(name));
2515 if (length < 2) invalid:
61b13cae 2516 [warnings addObject:UCLocalize("ILLEGAL_PACKAGE_IDENTIFIER")];
f464053e
JF
2517 else for (size_t i(0); i != length; ++i)
2518 if (
8944281a
JF
2519 /* XXX: technically this is not allowed */
2520 (name[i] < 'A' || name[i] > 'Z') &&
f464053e
JF
2521 (name[i] < 'a' || name[i] > 'z') &&
2522 (name[i] < '0' || name[i] > '9') &&
2523 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
2524 ) goto invalid;
2525
2526 if (strcmp(name, "cydia") != 0) {
2527 bool cydia = false;
419a9efd 2528 bool user = false;
8944281a 2529 bool _private = false;
f464053e
JF
2530 bool stash = false;
2531
8944281a
JF
2532 bool repository = [[self section] isEqualToString:@"Repositories"];
2533
f464053e
JF
2534 if (NSArray *files = [self files])
2535 for (NSString *file in files)
2536 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
2537 cydia = true;
419a9efd
JF
2538 else if (!user && [file isEqualToString:@"/User"])
2539 user = true;
8944281a
JF
2540 else if (!_private && [file isEqualToString:@"/private"])
2541 _private = true;
f464053e
JF
2542 else if (!stash && [file isEqualToString:@"/var/stash"])
2543 stash = true;
2544
8944281a
JF
2545 /* XXX: this is not sensitive enough. only some folders are valid. */
2546 if (cydia && !repository)
61b13cae 2547 [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"Cydia.app"]];
419a9efd
JF
2548 if (user)
2549 [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/User"]];
8944281a 2550 if (_private)
61b13cae 2551 [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/private"]];
f464053e 2552 if (stash)
61b13cae 2553 [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/var/stash"]];
f464053e
JF
2554 }
2555
2556 return [warnings count] == 0 ? nil : warnings;
2557}
2558
2559- (NSArray *) applications {
2560 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
2561
2562 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
2563
2564 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
2565 if (NSArray *files = [self files])
2566 for (NSString *file in files)
2567 if (application_r(file)) {
2568 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
2569 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
2570 if ([id isEqualToString:me])
2571 continue;
2572
2573 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
2574 if (display == nil)
2575 display = application_r[1];
2576
2577 NSString *bundle([file stringByDeletingLastPathComponent]);
2578 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
2579 if (icon == nil || [icon length] == 0)
2580 icon = @"icon.png";
2581 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
2582
2583 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
2584 [applications addObject:application];
2585
2586 [application addObject:id];
2587 [application addObject:display];
2588 [application addObject:url];
2589 }
2590
2591 return [applications count] == 0 ? nil : applications;
2592}
2593
e057ec05 2594- (Source *) source {
678b0973 2595 if (source_ == nil) {
a70cf746
JF
2596 @synchronized (database_) {
2597 if ([database_ era] != era_ || file_.end())
678b0973
JF
2598 source_ = (Source *) [NSNull null];
2599 else
2600 source_ = [([database_ getSource:file_.File()] ?: (Source *) [NSNull null]) retain];
a70cf746 2601 }
3d37fc0d
JF
2602 }
2603
678b0973 2604 return source_ == (Source *) [NSNull null] ? nil : source_;
b6ffa083
JF
2605}
2606
faf4eb4f
JF
2607- (NSString *) role {
2608 return role_;
2609}
2610
e057ec05
JF
2611- (BOOL) matches:(NSString *)text {
2612 if (text == nil)
2613 return NO;
b6ffa083 2614
e057ec05 2615 NSRange range;
b6ffa083 2616
3bddda52 2617 range = [[self id] rangeOfString:text options:MatchCompareOptions_];
e057ec05
JF
2618 if (range.location != NSNotFound)
2619 return YES;
b6ffa083 2620
3bddda52 2621 range = [[self name] rangeOfString:text options:MatchCompareOptions_];
e057ec05
JF
2622 if (range.location != NSNotFound)
2623 return YES;
b6ffa083 2624
5ec44e34 2625 range = [[self shortDescription] rangeOfString:text options:MatchCompareOptions_];
e057ec05 2626 if (range.location != NSNotFound)
5ec44e34 2627 return YES;
b6ffa083 2628
e057ec05
JF
2629 return NO;
2630}
b6ffa083 2631
faf4eb4f
JF
2632- (bool) hasSupportingRole {
2633 if (role_ == nil)
fa7bb92f 2634 return true;
faf4eb4f
JF
2635 if ([role_ isEqualToString:@"enduser"])
2636 return true;
2637 if ([Role_ isEqualToString:@"User"])
2638 return false;
2639 if ([role_ isEqualToString:@"hacker"])
2640 return true;
2641 if ([Role_ isEqualToString:@"Hacker"])
2642 return false;
2643 if ([role_ isEqualToString:@"developer"])
2644 return true;
2645 if ([Role_ isEqualToString:@"Developer"])
2646 return false;
2647 _assert(false);
fa7bb92f
JF
2648}
2649
2650- (BOOL) hasTag:(NSString *)tag {
2651 return tags_ == nil ? NO : [tags_ containsObject:tag];
2652}
2653
cb9c2100
JF
2654- (NSString *) primaryPurpose {
2655 for (NSString *tag in tags_)
2656 if ([tag hasPrefix:@"purpose::"])
2657 return [tag substringFromIndex:9];
2658 return nil;
2659}
2660
dbe0f181
JF
2661- (NSArray *) purposes {
2662 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
2663 for (NSString *tag in tags_)
2664 if ([tag hasPrefix:@"purpose::"])
2665 [purposes addObject:[tag substringFromIndex:9]];
2666 return [purposes count] == 0 ? nil : purposes;
2667}
2668
d8d9a65c
JF
2669- (bool) isCommercial {
2670 return [self hasTag:@"cydia::commercial"];
2671}
2672
dd9390c5
JF
2673- (CYString &) cyname {
2674 return name_.empty() ? id_ : name_;
6932575e
JF
2675}
2676
6932575e
JF
2677- (uint32_t) compareBySection:(NSArray *)sections {
2678 NSString *section([self section]);
2679 for (size_t i(0), e([sections count]); i != e; ++i) {
2680 if ([section isEqualToString:[[sections objectAtIndex:i] name]])
2681 return i;
e057ec05 2682 }
2d28b35a 2683
6932575e 2684 return _not(uint32_t);
e057ec05 2685}
2d28b35a 2686
f159ecd4
JF
2687- (uint32_t) compareForChanges {
2688 union {
2689 uint32_t key;
2d28b35a 2690
f159ecd4
JF
2691 struct {
2692 uint32_t timestamp : 30;
2693 uint32_t ignored : 1;
2694 uint32_t upgradable : 1;
2695 } bits;
2696 } value;
2d28b35a 2697
5c87d61b
JF
2698 bool upgradable([self upgradableAndEssential:YES]);
2699 value.bits.upgradable = upgradable ? 1 : 0;
b6ffa083 2700
5c87d61b 2701 if (upgradable) {
f159ecd4
JF
2702 value.bits.timestamp = 0;
2703 value.bits.ignored = [self ignored] ? 0 : 1;
2704 value.bits.upgradable = 1;
2705 } else {
2706 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
2707 value.bits.ignored = 0;
2708 value.bits.upgradable = 0;
e057ec05 2709 }
b6ffa083 2710
f159ecd4 2711 return _not(uint32_t) - value.key;
e057ec05 2712}
b6ffa083 2713
3ff1504e 2714- (void) clear {
30746bd9 2715@synchronized (database_) {
3ff1504e
JF
2716 pkgProblemResolver *resolver = [database_ resolver];
2717 resolver->Clear(iterator_);
30746bd9
JF
2718
2719 pkgCacheFile &cache([database_ cache]);
2720 cache->SetReInstall(iterator_, false);
2721 cache->MarkKeep(iterator_, false);
2722} }
3ff1504e 2723
e057ec05 2724- (void) install {
30746bd9 2725@synchronized (database_) {
e057ec05
JF
2726 pkgProblemResolver *resolver = [database_ resolver];
2727 resolver->Clear(iterator_);
2728 resolver->Protect(iterator_);
30746bd9 2729
e057ec05 2730 pkgCacheFile &cache([database_ cache]);
30746bd9 2731 cache->SetReInstall(iterator_, false);
e057ec05 2732 cache->MarkInstall(iterator_, false);
30746bd9 2733
e057ec05
JF
2734 pkgDepCache::StateCache &state((*cache)[iterator_]);
2735 if (!state.Install())
2736 cache->SetReInstall(iterator_, true);
30746bd9 2737} }
0f25fa58 2738
e057ec05 2739- (void) remove {
30746bd9 2740@synchronized (database_) {
e057ec05
JF
2741 pkgProblemResolver *resolver = [database_ resolver];
2742 resolver->Clear(iterator_);
e057ec05 2743 resolver->Remove(iterator_);
30746bd9
JF
2744 resolver->Protect(iterator_);
2745
2746 pkgCacheFile &cache([database_ cache]);
2747 cache->SetReInstall(iterator_, false);
2748 cache->MarkDelete(iterator_, true);
2749} }
b6ffa083 2750
7cf54836 2751- (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
3bddda52
JF
2752 _profile(Package$isUnfilteredAndSearchedForBy)
2753 bool value(true);
2754
2755 _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
2756 value &= [self unfiltered];
2757 _end
2758
2759 _profile(Package$isUnfilteredAndSearchedForBy$Match)
2760 value &= [self matches:search];
2761 _end
2762
7cf54836 2763 return value;
3bddda52 2764 _end
e057ec05 2765}
b6ffa083 2766
5ec44e34 2767- (bool) isUnfilteredAndSelectedForBy:(NSString *)search {
37455cf8
JF
2768 if ([search length] == 0)
2769 return false;
2770
5ec44e34
JF
2771 _profile(Package$isUnfilteredAndSelectedForBy)
2772 bool value(true);
2773
2774 _profile(Package$isUnfilteredAndSelectedForBy$Unfiltered)
2775 value &= [self unfiltered];
2776 _end
2777
2778 _profile(Package$isUnfilteredAndSelectedForBy$Match)
2779 value &= [[self name] compare:search options:MatchCompareOptions_ range:NSMakeRange(0, [search length])] == NSOrderedSame;
2780 _end
2781
2782 return value;
2783 _end
2784}
2785
1c220bde
JF
2786- (bool) isInstalledAndUnfiltered:(NSNumber *)number {
2787 return ![self uninstalled] && (![number boolValue] && ![role_ isEqualToString:@"cydia"] || [self unfiltered]);
e057ec05 2788}
b6ffa083 2789
6981ccdf 2790- (bool) isVisibleInSection:(NSString *)name {
1c220bde 2791 NSString *section([self section]);
965edd52 2792
1c220bde
JF
2793 return (
2794 name == nil ||
2795 section == nil && [name length] == 0 ||
2796 [name isEqualToString:section]
2797 ) && [self visible];
faf4eb4f
JF
2798}
2799
7cf54836
JF
2800- (bool) isVisibleInSource:(Source *)source {
2801 return [self source] == source && [self visible];
e057ec05 2802}
b6ffa083 2803
e057ec05
JF
2804@end
2805/* }}} */
2806/* Section Class {{{ */
2807@interface Section : NSObject {
2808 NSString *name_;
3bddda52 2809 unichar index_;
e057ec05
JF
2810 size_t row_;
2811 size_t count_;
6932575e 2812 NSString *localized_;
e057ec05 2813}
2d28b35a 2814
43b742af 2815- (NSComparisonResult) compareByLocalized:(Section *)section;
9ee296df
JF
2816- (Section *) initWithName:(NSString *)name localized:(NSString *)localized;
2817- (Section *) initWithName:(NSString *)name localize:(BOOL)localize;
2818- (Section *) initWithName:(NSString *)name row:(size_t)row localize:(BOOL)localize;
3bddda52 2819- (Section *) initWithIndex:(unichar)index row:(size_t)row;
e057ec05 2820- (NSString *) name;
3bddda52 2821- (unichar) index;
6932575e 2822
e057ec05
JF
2823- (size_t) row;
2824- (size_t) count;
6932575e
JF
2825
2826- (void) addToRow;
e057ec05 2827- (void) addToCount;
a933cee2 2828
6932575e 2829- (void) setCount:(size_t)count;
43b742af 2830- (NSString *) localized;
6932575e 2831
e057ec05 2832@end
a933cee2 2833
e057ec05 2834@implementation Section
a933cee2 2835
e057ec05
JF
2836- (void) dealloc {
2837 [name_ release];
6932575e
JF
2838 if (localized_ != nil)
2839 [localized_ release];
e057ec05
JF
2840 [super dealloc];
2841}
a933cee2 2842
43b742af 2843- (NSComparisonResult) compareByLocalized:(Section *)section {
9ee296df
JF
2844 NSString *lhs(localized_);
2845 NSString *rhs([section localized]);
fa7bb92f 2846
9ee296df 2847 /*if ([lhs length] != 0 && [rhs length] != 0) {
fa7bb92f
JF
2848 unichar lhc = [lhs characterAtIndex:0];
2849 unichar rhc = [rhs characterAtIndex:0];
2850
2851 if (isalpha(lhc) && !isalpha(rhc))
2852 return NSOrderedAscending;
2853 else if (!isalpha(lhc) && isalpha(rhc))
2854 return NSOrderedDescending;
9ee296df 2855 }*/
fa7bb92f 2856
9c4e0cbe 2857 return [lhs compare:rhs options:LaxCompareOptions_];
fa7bb92f
JF
2858}
2859
9ee296df
JF
2860- (Section *) initWithName:(NSString *)name localized:(NSString *)localized {
2861 if ((self = [self initWithName:name localize:NO]) != nil) {
2862 if (localized != nil)
2863 localized_ = [localized retain];
2864 } return self;
2865}
2866
2867- (Section *) initWithName:(NSString *)name localize:(BOOL)localize {
2868 return [self initWithName:name row:0 localize:localize];
fa7bb92f
JF
2869}
2870
9ee296df 2871- (Section *) initWithName:(NSString *)name row:(size_t)row localize:(BOOL)localize {
e057ec05
JF
2872 if ((self = [super init]) != nil) {
2873 name_ = [name retain];
3bddda52
JF
2874 index_ = '\0';
2875 row_ = row;
9ee296df
JF
2876 if (localize)
2877 localized_ = [LocalizeSection(name_) retain];
3bddda52
JF
2878 } return self;
2879}
2880
6932575e 2881/* XXX: localize the index thingees */
3bddda52
JF
2882- (Section *) initWithIndex:(unichar)index row:(size_t)row {
2883 if ((self = [super init]) != nil) {
5d8f1006 2884 name_ = [[NSString stringWithCharacters:&index length:1] retain];
3bddda52 2885 index_ = index;
e057ec05 2886 row_ = row;
a933cee2
JF
2887 } return self;
2888}
2889
e057ec05
JF
2890- (NSString *) name {
2891 return name_;
2892}
2893
3bddda52
JF
2894- (unichar) index {
2895 return index_;
2896}
2897
e057ec05
JF
2898- (size_t) row {
2899 return row_;
2900}
2901
2902- (size_t) count {
2903 return count_;
2904}
2905
6932575e
JF
2906- (void) addToRow {
2907 ++row_;
2908}
2909
e057ec05
JF
2910- (void) addToCount {
2911 ++count_;
2912}
2913
6932575e
JF
2914- (void) setCount:(size_t)count {
2915 count_ = count;
2916}
2917
2918- (NSString *) localized {
2919 return localized_;
2920}
2921
a933cee2
JF
2922@end
2923/* }}} */
2924
6981ccdf 2925static NSString *Colon_;
835b451e 2926static NSString *Elision_;
6981ccdf
JF
2927static NSString *Error_;
2928static NSString *Warning_;
2929
e057ec05
JF
2930/* Database Implementation {{{ */
2931@implementation Database
1cb11c5f 2932
dbe0f181
JF
2933+ (Database *) sharedInstance {
2934 static Database *instance;
2935 if (instance == nil)
2936 instance = [[Database alloc] init];
2937 return instance;
2938}
2939
a70cf746
JF
2940- (unsigned) era {
2941 return era_;
2942}
2943
e057ec05 2944- (void) dealloc {
a591888a 2945 // XXX: actually implement this thing
e057ec05 2946 _assert(false);
6932575e
JF
2947 NSRecycleZone(zone_);
2948 // XXX: malloc_destroy_zone(zone_);
2949 apr_pool_destroy(pool_);
e057ec05
JF
2950 [super dealloc];
2951}
1cb11c5f 2952
f464053e 2953- (void) _readCydia:(NSNumber *)fd { _pooled
d72d91aa
JF
2954 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2955 std::istream is(&ib);
2956 std::string line;
2957
03d01f0e
JF
2958 static Pcre finish_r("^finish:([^:]*)$");
2959
d72d91aa
JF
2960 while (std::getline(is, line)) {
2961 const char *data(line.c_str());
03d01f0e 2962 size_t size = line.size();
cb9c2100 2963 lprintf("C:%s\n", data);
03d01f0e
JF
2964
2965 if (finish_r(data, size)) {
2966 NSString *finish = finish_r[1];
2967 int index = [Finishes_ indexOfObject:finish];
2968 if (index != INT_MAX && index > Finish_)
2969 Finish_ = index;
2970 }
d72d91aa
JF
2971 }
2972
6981ccdf 2973 _assume(false);
d72d91aa
JF
2974}
2975
f464053e 2976- (void) _readStatus:(NSNumber *)fd { _pooled
e057ec05
JF
2977 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2978 std::istream is(&ib);
2979 std::string line;
1cb11c5f 2980
faf4eb4f
JF
2981 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2982 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1cb11c5f 2983
e057ec05
JF
2984 while (std::getline(is, line)) {
2985 const char *data(line.c_str());
6981ccdf 2986 size_t size(line.size());
cb9c2100 2987 lprintf("S:%s\n", data);
1cb11c5f 2988
965edd52
JF
2989 if (conffile_r(data, size)) {
2990 [delegate_ setConfigurationData:conffile_r[1]];
2991 } else if (strncmp(data, "status: ", 8) == 0) {
2992 NSString *string = [NSString stringWithUTF8String:(data + 8)];
e057ec05 2993 [delegate_ setProgressTitle:string];
965edd52 2994 } else if (pmstatus_r(data, size)) {
238b07ce
JF
2995 std::string type([pmstatus_r[1] UTF8String]);
2996 NSString *id = pmstatus_r[2];
2997
965edd52
JF
2998 float percent([pmstatus_r[3] floatValue]);
2999 [delegate_ setProgressPercent:(percent / 100)];
3000
3001 NSString *string = pmstatus_r[4];
965edd52
JF
3002
3003 if (type == "pmerror")
6981ccdf 3004 [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:)
238b07ce
JF
3005 withObject:[NSArray arrayWithObjects:string, id, nil]
3006 waitUntilDone:YES
3007 ];
907a35d6 3008 else if (type == "pmstatus") {
965edd52 3009 [delegate_ setProgressTitle:string];
907a35d6 3010 } else if (type == "pmconffile")
965edd52 3011 [delegate_ setConfigurationData:string];
6981ccdf
JF
3012 else
3013 lprintf("E:unknown pmstatus\n");
3014 } else
3015 lprintf("E:unknown status\n");
e057ec05 3016 }
1cb11c5f 3017
6981ccdf 3018 _assume(false);
e057ec05 3019}
1cb11c5f 3020
f464053e 3021- (void) _readOutput:(NSNumber *)fd { _pooled
e057ec05
JF
3022 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
3023 std::istream is(&ib);
3024 std::string line;
3025
965edd52 3026 while (std::getline(is, line)) {
cb9c2100 3027 lprintf("O:%s\n", line.c_str());
2a987aa5 3028 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
965edd52 3029 }
e057ec05 3030
6981ccdf 3031 _assume(false);
1cb11c5f
JF
3032}
3033
7600bd69
JF
3034- (FILE *) input {
3035 return input_;
3036}
3037
e057ec05 3038- (Package *) packageWithName:(NSString *)name {
f3d8816a 3039@synchronized (self) {
965edd52
JF
3040 if (static_cast<pkgDepCache *>(cache_) == NULL)
3041 return nil;
e057ec05 3042 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
6932575e 3043 return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:pool_ database:self];
ca06bb0e 3044} }
e057ec05 3045
9b62701b 3046- (id) init {
1cb11c5f 3047 if ((self = [super init]) != nil) {
965edd52 3048 policy_ = NULL;
e057ec05
JF
3049 records_ = NULL;
3050 resolver_ = NULL;
3051 fetcher_ = NULL;
3052 lock_ = NULL;
1cb11c5f 3053
6932575e
JF
3054 zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
3055 apr_pool_create(&pool_, NULL);
3056
777744da 3057 packages_ = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
1cb11c5f 3058
e057ec05 3059 int fds[2];
1cb11c5f 3060
d72d91aa
JF
3061 _assert(pipe(fds) != -1);
3062 cydiafd_ = fds[1];
3063
3064 _config->Set("APT::Keep-Fds::", cydiafd_);
03d01f0e 3065 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
d72d91aa
JF
3066
3067 [NSThread
3068 detachNewThreadSelector:@selector(_readCydia:)
3069 toTarget:self
a591888a 3070 withObject:[NSNumber numberWithInt:fds[0]]
d72d91aa
JF
3071 ];
3072
e057ec05
JF
3073 _assert(pipe(fds) != -1);
3074 statusfd_ = fds[1];
1cb11c5f 3075
e057ec05
JF
3076 [NSThread
3077 detachNewThreadSelector:@selector(_readStatus:)
3078 toTarget:self
a591888a 3079 withObject:[NSNumber numberWithInt:fds[0]]
e057ec05 3080 ];
1cb11c5f 3081
7600bd69
JF
3082 _assert(pipe(fds) != -1);
3083 _assert(dup2(fds[0], 0) != -1);
3084 _assert(close(fds[0]) != -1);
3085
3086 input_ = fdopen(fds[1], "a");
3087
e057ec05
JF
3088 _assert(pipe(fds) != -1);
3089 _assert(dup2(fds[1], 1) != -1);
3090 _assert(close(fds[1]) != -1);
3091
3092 [NSThread
3093 detachNewThreadSelector:@selector(_readOutput:)
3094 toTarget:self
a591888a 3095 withObject:[NSNumber numberWithInt:fds[0]]
e057ec05 3096 ];
1cb11c5f
JF
3097 } return self;
3098}
3099
e057ec05
JF
3100- (pkgCacheFile &) cache {
3101 return cache_;
1cb11c5f
JF
3102}
3103
965edd52
JF
3104- (pkgDepCache::Policy *) policy {
3105 return policy_;
3106}
3107
e057ec05
JF
3108- (pkgRecords *) records {
3109 return records_;
1cb11c5f
JF
3110}
3111
e057ec05
JF
3112- (pkgProblemResolver *) resolver {
3113 return resolver_;
1cb11c5f
JF
3114}
3115
e057ec05
JF
3116- (pkgAcquire &) fetcher {
3117 return *fetcher_;
1cb11c5f
JF
3118}
3119
ce09fc27
JF
3120- (pkgSourceList &) list {
3121 return *list_;
3122}
3123
e057ec05 3124- (NSArray *) packages {
777744da 3125 return (NSArray *) packages_;
1cb11c5f
JF
3126}
3127
faf4eb4f 3128- (NSArray *) sources {
631a0a1e
JF
3129 NSMutableArray *sources([NSMutableArray arrayWithCapacity:sources_.size()]);
3130 for (SourceMap::const_iterator i(sources_.begin()); i != sources_.end(); ++i)
3131 [sources addObject:i->second];
3132 return sources;
faf4eb4f
JF
3133}
3134
f464053e
JF
3135- (NSArray *) issues {
3136 if (cache_->BrokenCount() == 0)
3137 return nil;
3138
3139 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
3140
777744da 3141 for (Package *package in [self packages]) {
f464053e
JF
3142 if (![package broken])
3143 continue;
3144 pkgCache::PkgIterator pkg([package iterator]);
3145
3146 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
3147 [entry addObject:[package name]];
3148 [issues addObject:entry];
3149
3150 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
3151 if (ver.end())
3152 continue;
3153
3154 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
3155 pkgCache::DepIterator start;
3156 pkgCache::DepIterator end;
3157 dep.GlobOr(start, end); // ++dep
3158
3159 if (!cache_->IsImportantDep(end))
3160 continue;
3161 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
3162 continue;
3163
3164 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
3165 [entry addObject:failure];
3166 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
3167
e4765783
JF
3168 NSString *name([NSString stringWithUTF8String:start.TargetPkg().Name()]);
3169 if (Package *package = [self packageWithName:name])
3170 name = [package name];
3171 [failure addObject:name];
f464053e
JF
3172
3173 pkgCache::PkgIterator target(start.TargetPkg());
3174 if (target->ProvidesList != 0)
3175 [failure addObject:@"?"];
3176 else {
3177 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
3178 if (!ver.end())
3179 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
3180 else if (!cache_[target].CandidateVerIter(cache_).end())
3181 [failure addObject:@"-"];
3182 else if (target->ProvidesList == 0)
3183 [failure addObject:@"!"];
3184 else
3185 [failure addObject:@"%"];
3186 }
3187
3188 _forever {
3189 if (start.TargetVer() != 0)
3190 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
3191 if (start == end)
3192 break;
3193 ++start;
3194 }
3195 }
3196 }
3197
3198 return issues;
3199}
3200
6981ccdf
JF
3201- (bool) popErrorWithTitle:(NSString *)title {
3202 bool fatal(false);
3203 std::string message;
3204
3205 while (!_error->empty()) {
3206 std::string error;
3207 bool warning(!_error->PopMessage(error));
3208 if (!warning)
3209 fatal = true;
3210 for (;;) {
3211 size_t size(error.size());
3212 if (size == 0 || error[size - 1] != '\n')
3213 break;
3214 error.resize(size - 1);
3215 }
3216 lprintf("%c:[%s]\n", warning ? 'W' : 'E', error.c_str());
3217
3218 if (!message.empty())
3219 message += "\n\n";
3220 message += error;
3221 }
3222
281f523c 3223 if (fatal && !message.empty())
6981ccdf
JF
3224 [delegate_ _setProgressError:[NSString stringWithUTF8String:message.c_str()] withTitle:[NSString stringWithFormat:Colon_, fatal ? Error_ : Warning_, title]];
3225
3226 return fatal;
3227}
3228
3229- (bool) popErrorWithTitle:(NSString *)title forOperation:(bool)success {
3230 return [self popErrorWithTitle:title] || !success;
3231}
3232
7831584c 3233- (void) reloadData { CYPoolStart() {
f3d8816a
JF
3234@synchronized (self) {
3235 ++era_;
a70cf746 3236
777744da
JF
3237 CFArrayApplyFunction(packages_, CFRangeMake(0, CFArrayGetCount(packages_)), reinterpret_cast<CFArrayApplierFunction>(&CFRelease), NULL);
3238 CFArrayRemoveAllValues(packages_);
3239
98fb9119
JF
3240 sources_.clear();
3241
e057ec05 3242 _error->Discard();
965edd52 3243
e057ec05 3244 delete list_;
965edd52 3245 list_ = NULL;
e057ec05
JF
3246 manager_ = NULL;
3247 delete lock_;
965edd52 3248 lock_ = NULL;
e057ec05 3249 delete fetcher_;
965edd52 3250 fetcher_ = NULL;
e057ec05 3251 delete resolver_;
965edd52 3252 resolver_ = NULL;
e057ec05 3253 delete records_;
965edd52
JF
3254 records_ = NULL;
3255 delete policy_;
3256 policy_ = NULL;
e057ec05 3257
98fb9119
JF
3258 if (now_ != nil) {
3259 [now_ release];
3260 now_ = nil;
3261 }
3262
965edd52 3263 cache_.Close();
7600bd69 3264
6932575e
JF
3265 apr_pool_clear(pool_);
3266 NSRecycleZone(zone_);
3267
3e3977a2
JF
3268 int chk(creat("/tmp/cydia.chk", 0644));
3269 if (chk != -1)
3270 close(chk);
3271
6981ccdf
JF
3272 NSString *title(UCLocalize("DATABASE"));
3273
907a35d6 3274 _trace();
6981ccdf 3275 if (!cache_.Open(progress_, true)) { pop:
7600bd69 3276 std::string error;
6981ccdf 3277 bool warning(!_error->PopMessage(error));
cb9c2100 3278 lprintf("cache_.Open():[%s]\n", error.c_str());
965edd52
JF
3279
3280 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
3281 [delegate_ repairWithSelector:@selector(configure)];
3282 else if (error == "The package lists or status file could not be parsed or opened.")
3283 [delegate_ repairWithSelector:@selector(update)];
fc19e583
JF
3284 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
3285 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
3286 // else if (error == "The list of sources could not be read.")
6981ccdf
JF
3287 else
3288 [delegate_ _setProgressError:[NSString stringWithUTF8String:error.c_str()] withTitle:[NSString stringWithFormat:Colon_, warning ? Warning_ : Error_, title]];
965edd52 3289
6981ccdf
JF
3290 if (warning)
3291 goto pop;
3292 _error->Discard();
965edd52 3293 return;
e057ec05 3294 }
907a35d6 3295 _trace();
e057ec05 3296
3e3977a2
JF
3297 unlink("/tmp/cydia.chk");
3298
e057ec05
JF
3299 now_ = [[NSDate date] retain];
3300
965edd52 3301 policy_ = new pkgDepCache::Policy();
e057ec05
JF
3302 records_ = new pkgRecords(cache_);
3303 resolver_ = new pkgProblemResolver(cache_);
3304 fetcher_ = new pkgAcquire(&status_);
3305 lock_ = NULL;
3306
3307 list_ = new pkgSourceList();
6981ccdf
JF
3308 if ([self popErrorWithTitle:title forOperation:list_->ReadMainList()])
3309 return;
3310
3311 if (cache_->DelCount() != 0 || cache_->InstCount() != 0) {
3312 [delegate_ _setProgressError:@"COUNTS_NONZERO_EX" withTitle:title];
3313 return;
3314 }
e057ec05 3315
6981ccdf
JF
3316 if ([self popErrorWithTitle:title forOperation:pkgApplyStatus(cache_)])
3317 return;
3319715b
JF
3318
3319 if (cache_->BrokenCount() != 0) {
6981ccdf
JF
3320 if ([self popErrorWithTitle:title forOperation:pkgFixBroken(cache_)])
3321 return;
3322
3323 if (cache_->BrokenCount() != 0) {
3324 [delegate_ _setProgressError:@"STILL_BROKEN_EX" withTitle:title];
3325 return;
3326 }
3327
3328 if ([self popErrorWithTitle:title forOperation:pkgMinimizeUpgrade(cache_)])
3329 return;
3319715b
JF
3330 }
3331
631a0a1e
JF
3332 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
3333 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
3334 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
3335 // XXX: this could be more intelligent
3336 if (dynamic_cast<debPackagesIndex *>(*index) != NULL) {
3337 pkgCache::PkgFileIterator cached((*index)->FindInCache(cache_));
3338 if (!cached.end())
b8b1cfd0 3339 sources_[cached->ID] = [[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease];
631a0a1e 3340 }
e057ec05 3341 }
631a0a1e 3342
43b742af 3343 {
9ee296df 3344 /*std::vector<Package *> packages;
43b742af 3345 packages.reserve(std::max(10000U, [packages_ count] + 1000));
43b742af 3346 [packages_ release];
9ee296df
JF
3347 packages_ = nil;*/
3348
43b742af
JF
3349 _trace();
3350
3351 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
3352 if (Package *package = [Package packageWithIterator:iterator withZone:zone_ inPool:pool_ database:self])
9ee296df 3353 //packages.push_back(package);
777744da 3354 CFArrayAppendValue(packages_, [package retain]);
43b742af
JF
3355
3356 _trace();
3357
9ee296df 3358 /*if (packages.empty())
43b742af
JF
3359 packages_ = [[NSArray alloc] init];
3360 else
3361 packages_ = [[NSArray alloc] initWithObjects:&packages.front() count:packages.size()];
9ee296df
JF
3362 _trace();*/
3363
777744da
JF
3364 [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(16)];
3365 [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(4)];
3366 [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(0)];
9ee296df
JF
3367
3368 /*_trace();
3369 PrintTimes();
3370 _trace();*/
3371
3372 _trace();
3373
3374 /*if (!packages.empty())
3375 CFQSortArray(&packages.front(), packages.size(), sizeof(packages.front()), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare_), NULL);*/
3376 //std::sort(packages.begin(), packages.end(), PackageNameOrdering());
3377
66abcbb0
JF
3378 //CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
3379
777744da 3380 CFArrayInsertionSortValues(packages_, CFRangeMake(0, CFArrayGetCount(packages_)), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
9ee296df
JF
3381
3382 //[packages_ sortUsingFunction:reinterpret_cast<NSComparisonResult (*)(id, id, void *)>(&PackageNameCompare) context:NULL];
43b742af
JF
3383
3384 _trace();
3385 }
d11754e5 3386} } CYPoolEnd() _trace(); }
1cb11c5f 3387
30746bd9
JF
3388- (void) clear {
3389@synchronized (self) {
3390 delete resolver_;
3391 resolver_ = new pkgProblemResolver(cache_);
3392
3393 for (pkgCache::PkgIterator iterator(cache_->PkgBegin()); !iterator.end(); ++iterator) {
3394 if (!cache_[iterator].Keep()) {
3395 cache_->MarkKeep(iterator, false);
3396 cache_->SetReInstall(iterator, false);
3397 }
3398 }
3399} }
3400
965edd52
JF
3401- (void) configure {
3402 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
3403 system([dpkg UTF8String]);
3404}
3405
6981ccdf
JF
3406- (bool) clean {
3407 // XXX: I don't remember this condition
d72d91aa 3408 if (lock_ != NULL)
6981ccdf 3409 return false;
d72d91aa
JF
3410
3411 FileFd Lock;
3412 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
6981ccdf
JF
3413
3414 NSString *title(UCLocalize("CLEAN_ARCHIVES"));
3415
3416 if ([self popErrorWithTitle:title])
3417 return false;
d72d91aa
JF
3418
3419 pkgAcquire fetcher;
3420 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
3421
3319715b
JF
3422 class LogCleaner :
3423 public pkgArchiveCleaner
3424 {
d72d91aa
JF
3425 protected:
3426 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
3319715b 3427 unlink(File);
d72d91aa
JF
3428 }
3429 } cleaner;
3430
6981ccdf
JF
3431 if ([self popErrorWithTitle:title forOperation:cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)])
3432 return false;
3433
3434 return true;
d72d91aa
JF
3435}
3436
6981ccdf 3437- (bool) prepare {
e4765783
JF
3438 fetcher_->Shutdown();
3439
e057ec05
JF
3440 pkgRecords records(cache_);
3441
3442 lock_ = new FileFd();
3443 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
6981ccdf
JF
3444
3445 NSString *title(UCLocalize("PREPARE_ARCHIVES"));
3446
3447 if ([self popErrorWithTitle:title])
3448 return false;
e057ec05
JF
3449
3450 pkgSourceList list;
6981ccdf
JF
3451 if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
3452 return false;
e057ec05
JF
3453
3454 manager_ = (_system->CreatePM(cache_));
6981ccdf
JF
3455 if ([self popErrorWithTitle:title forOperation:manager_->GetArchives(fetcher_, &list, &records)])
3456 return false;
3457
3458 return true;
1cb11c5f
JF
3459}
3460
e057ec05 3461- (void) perform {
6981ccdf
JF
3462 NSString *title(UCLocalize("PERFORM_SELECTIONS"));
3463
113c9b62
JF
3464 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
3465 pkgSourceList list;
6981ccdf
JF
3466 if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
3467 return;
113c9b62
JF
3468 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
3469 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
3470 }
8993ad57 3471
2d7f7dea
JF
3472 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
3473 _trace();
e057ec05 3474 return;
2d7f7dea
JF
3475 }
3476
3477 bool failed = false;
3478 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
3479 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
3480 continue;
9050015e
JF
3481 if ((*item)->Status == pkgAcquire::Item::StatIdle)
3482 continue;
2d7f7dea
JF
3483
3484 std::string uri = (*item)->DescURI();
3485 std::string error = (*item)->ErrorText;
3486
cb9c2100 3487 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
2d7f7dea
JF
3488 failed = true;
3489
6981ccdf 3490 [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:)
ce09fc27
JF
3491 withObject:[NSArray arrayWithObjects:
3492 [NSString stringWithUTF8String:error.c_str()],
3493 nil]
2d7f7dea
JF
3494 waitUntilDone:YES
3495 ];
3496 }
3497
3498 if (failed) {
3499 _trace();
3500 return;
3501 }
e057ec05
JF
3502
3503 _system->UnLock();
3504 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
3505
2d7f7dea
JF
3506 if (_error->PendingError()) {
3507 _trace();
e057ec05 3508 return;
2d7f7dea
JF
3509 }
3510
3511 if (result == pkgPackageManager::Failed) {
3512 _trace();
e057ec05 3513 return;
2d7f7dea
JF
3514 }
3515
3516 if (result != pkgPackageManager::Completed) {
3517 _trace();
e057ec05 3518 return;
2d7f7dea 3519 }
8993ad57 3520
113c9b62
JF
3521 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
3522 pkgSourceList list;
6981ccdf
JF
3523 if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
3524 return;
113c9b62
JF
3525 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
3526 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
3527 }
3528
3529 if (![before isEqualToArray:after])
3530 [self update];
1cb11c5f
JF
3531}
3532
6981ccdf
JF
3533- (bool) upgrade {
3534 NSString *title(UCLocalize("UPGRADE"));
3535 if ([self popErrorWithTitle:title forOperation:pkgDistUpgrade(cache_)])
3536 return false;
3537 return true;
1951a333
JF
3538}
3539
e057ec05
JF
3540- (void) update {
3541 [self updateWithStatus:status_];
3542}
686e302f 3543
6981ccdf
JF
3544- (void) updateWithStatus:(Status &)status {
3545 _transient NSObject<ProgressDelegate> *delegate(status.getDelegate());
3546 NSString *title(UCLocalize("REFRESHING_DATA"));
3547
e057ec05 3548 pkgSourceList list;
6981ccdf
JF
3549 if (!list.ReadMainList())
3550 [delegate _setProgressError:@"Unable to read source list." withTitle:title];
686e302f 3551
e057ec05
JF
3552 FileFd lock;
3553 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
6981ccdf
JF
3554 if ([self popErrorWithTitle:title])
3555 return;
f30eaf83 3556
6981ccdf 3557 if ([self popErrorWithTitle:title forOperation:ListUpdate(status, list, PulseInterval_)])
9b62701b
GP
3558 /* XXX: ignore this because users suck and don't understand why refreshing is important: return */
3559 /* XXX: why the hell is an empty if statement a clang error? */ (void) 0;
e057ec05 3560
419a9efd
JF
3561 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
3562 Changed_ = true;
686e302f
JF
3563}
3564
e057ec05
JF
3565- (void) setDelegate:(id)delegate {
3566 delegate_ = delegate;
3567 status_.setDelegate(delegate);
3568 progress_.setDelegate(delegate);
3569}
686e302f 3570
3e3977a2 3571- (Source *) getSource:(pkgCache::PkgFileIterator)file {
1636cf29
JF
3572 SourceMap::const_iterator i(sources_.find(file->ID));
3573 return i == sources_.end() ? nil : i->second;
a933cee2
JF
3574}
3575
e057ec05
JF
3576@end
3577/* }}} */
686e302f 3578
f441e717 3579/* Confirmation Controller {{{ */
61b13cae
JF
3580bool DepSubstrate(const pkgCache::VerIterator &iterator) {
3581 if (!iterator.end())
3582 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
3583 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
3584 continue;
3585 pkgCache::PkgIterator package(dep.TargetPkg());
3586 if (package.end())
3587 continue;
3588 if (strcmp(package.Name(), "mobilesubstrate") == 0)
3589 return true;
3590 }
3591
3592 return false;
cb9c2100 3593}
9ae52960 3594/* }}} */
cb9c2100 3595
61b13cae
JF
3596/* Web Scripting {{{ */
3597@interface CydiaObject : NSObject {
3598 id indirect_;
a591888a 3599 _transient id delegate_;
61b13cae 3600}
cb9c2100 3601
61b13cae 3602- (id) initWithDelegate:(IndirectDelegate *)indirect;
cb9c2100
JF
3603@end
3604
61b13cae 3605@implementation CydiaObject
cb9c2100
JF
3606
3607- (void) dealloc {
61b13cae 3608 [indirect_ release];
cb9c2100
JF
3609 [super dealloc];
3610}
3611
61b13cae
JF
3612- (id) initWithDelegate:(IndirectDelegate *)indirect {
3613 if ((self = [super init]) != nil) {
3614 indirect_ = [indirect retain];
3615 } return self;
3616}
3617
daf7f6e2
JF
3618- (void) setDelegate:(id)delegate {
3619 delegate_ = delegate;
3620}
3621
61b13cae
JF
3622+ (NSArray *) _attributeKeys {
3623 return [NSArray arrayWithObjects:@"device", @"firewire", @"imei", @"mac", @"serial", nil];
3624}
3625
3626- (NSArray *) attributeKeys {
3627 return [[self class] _attributeKeys];
cb9c2100
JF
3628}
3629
61b13cae
JF
3630+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
3631 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
cb9c2100 3632}
61b13cae
JF
3633
3634- (NSString *) device {
3635 return [[UIDevice currentDevice] uniqueIdentifier];
cb9c2100
JF
3636}
3637
61b13cae
JF
3638#if 0 // XXX: implement!
3639- (NSString *) mac {
3640 if (![indirect_ promptForSensitive:@"Mac Address"])
3641 return nil;
f464053e
JF
3642}
3643
61b13cae
JF
3644- (NSString *) serial {
3645 if (![indirect_ promptForSensitive:@"Serial #"])
3646 return nil;
3647}
56e10908 3648
61b13cae
JF
3649- (NSString *) firewire {
3650 if (![indirect_ promptForSensitive:@"Firewire GUID"])
3651 return nil;
f464053e
JF
3652}
3653
61b13cae
JF
3654- (NSString *) imei {
3655 if (![indirect_ promptForSensitive:@"IMEI"])
3656 return nil;
3657}
3658#endif
3659
3660+ (NSString *) webScriptNameForSelector:(SEL)selector {
3661 if (selector == @selector(close))
3662 return @"close";
22b21e43
JF
3663 else if (selector == @selector(getInstalledPackages))
3664 return @"getInstalledPackages";
61b13cae
JF
3665 else if (selector == @selector(getPackageById:))
3666 return @"getPackageById";
daf7f6e2
JF
3667 else if (selector == @selector(installPackages:))
3668 return @"installPackages";
61b13cae
JF
3669 else if (selector == @selector(setButtonImage:withStyle:toFunction:))
3670 return @"setButtonImage";
3671 else if (selector == @selector(setButtonTitle:withStyle:toFunction:))
3672 return @"setButtonTitle";
61b13cae
JF
3673 else if (selector == @selector(setPopupHook:))
3674 return @"setPopupHook";
3675 else if (selector == @selector(setSpecial:))
3676 return @"setSpecial";
37455cf8
JF
3677 else if (selector == @selector(setToken:))
3678 return @"setToken";
61b13cae
JF
3679 else if (selector == @selector(setViewportWidth:))
3680 return @"setViewportWidth";
3681 else if (selector == @selector(supports:))
3682 return @"supports";
3683 else if (selector == @selector(stringWithFormat:arguments:))
3684 return @"format";
3685 else if (selector == @selector(localizedStringForKey:value:table:))
3686 return @"localize";
3687 else if (selector == @selector(du:))
3688 return @"du";
3689 else if (selector == @selector(statfs:))
3690 return @"statfs";
cb9c2100 3691 else
61b13cae
JF
3692 return nil;
3693}
3694
3695+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
3696 return [self webScriptNameForSelector:selector] == nil;
cb9c2100
JF
3697}
3698
61b13cae
JF
3699- (BOOL) supports:(NSString *)feature {
3700 return [feature isEqualToString:@"window.open"];
3701}
cb9c2100 3702
22b21e43
JF
3703- (NSArray *) getInstalledPackages {
3704 NSArray *packages([[Database sharedInstance] packages]);
3705 NSMutableArray *installed([NSMutableArray arrayWithCapacity:[packages count]]);
daf7f6e2 3706 for (Package *package in packages)
22b21e43
JF
3707 if ([package installed] != nil)
3708 [installed addObject:package];
3709 return installed;
3710}
3711
61b13cae 3712- (Package *) getPackageById:(NSString *)id {
e32f0fcb
JF
3713 Package *package([[Database sharedInstance] packageWithName:id]);
3714 [package parse];
3715 return package;
61b13cae
JF
3716}
3717
3718- (NSArray *) statfs:(NSString *)path {
3719 struct statfs stat;
3720
3721 if (path == nil || statfs([path UTF8String], &stat) == -1)
3722 return nil;
3723
3724 return [NSArray arrayWithObjects:
3725 [NSNumber numberWithUnsignedLong:stat.f_bsize],
3726 [NSNumber numberWithUnsignedLong:stat.f_blocks],
3727 [NSNumber numberWithUnsignedLong:stat.f_bfree],
3728 nil];
3729}
3730
3731- (NSNumber *) du:(NSString *)path {
3732 NSNumber *value(nil);
3733
3734 int fds[2];
3735 _assert(pipe(fds) != -1);
3736
3737 pid_t pid(ExecFork());
3738 if (pid == 0) {
3739 _assert(dup2(fds[1], 1) != -1);
3740 _assert(close(fds[0]) != -1);
3741 _assert(close(fds[1]) != -1);
3742 /* XXX: this should probably not use du */
3743 execl("/usr/libexec/cydia/du", "du", "-s", [path UTF8String], NULL);
3744 exit(1);
3745 _assert(false);
3746 }
3747
3748 _assert(close(fds[1]) != -1);
3749
3750 if (FILE *du = fdopen(fds[0], "r")) {
3751 char line[1024];
3752 while (fgets(line, sizeof(line), du) != NULL) {
3753 size_t length(strlen(line));
3754 while (length != 0 && line[length - 1] == '\n')
3755 line[--length] = '\0';
3756 if (char *tab = strchr(line, '\t')) {
3757 *tab = '\0';
3758 value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)];
3759 }
3760 }
3761
3762 fclose(du);
3763 } else _assert(close(fds[0]));
3764
3765 int status;
3766 wait:
3767 if (waitpid(pid, &status, 0) == -1)
3768 if (errno == EINTR)
3769 goto wait;
3770 else _assert(false);
3771
3772 return value;
3773}
3774
3775- (void) close {
3776 [indirect_ close];
3777}
3778
daf7f6e2
JF
3779- (void) installPackages:(NSArray *)packages {
3780 [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO];
3781}
3782
61b13cae
JF
3783- (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
3784 [indirect_ setButtonImage:button withStyle:style toFunction:function];
3785}
3786
3787- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
3788 [indirect_ setButtonTitle:button withStyle:style toFunction:function];
3789}
3790
3791- (void) setSpecial:(id)function {
3792 [indirect_ setSpecial:function];
3793}
3794
37455cf8
JF
3795- (void) setToken:(NSString *)token {
3796 if (Token_ != nil)
3797 [Token_ release];
3798 Token_ = [token retain];
3799
3800 [Metadata_ setObject:Token_ forKey:@"Token"];
3801 Changed_ = true;
3802}
3803
61b13cae
JF
3804- (void) setPopupHook:(id)function {
3805 [indirect_ setPopupHook:function];
3806}
3807
3808- (void) setViewportWidth:(float)width {
3809 [indirect_ setViewportWidth:width];
3810}
3811
3812- (NSString *) stringWithFormat:(NSString *)format arguments:(WebScriptObject *)arguments {
3813 //NSLog(@"SWF:\"%@\" A:%@", format, [arguments description]);
3814 unsigned count([arguments count]);
3815 id values[count];
3816 for (unsigned i(0); i != count; ++i)
3817 values[i] = [arguments objectAtIndex:i];
9b62701b 3818 return [[[NSString alloc] initWithFormat:format arguments:*(reinterpret_cast<va_list *>(&values))] autorelease];
61b13cae
JF
3819}
3820
3821- (NSString *) localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)table {
e1316e10
JF
3822 if (reinterpret_cast<id>(value) == [WebUndefined undefined])
3823 value = nil;
61b13cae
JF
3824 if (reinterpret_cast<id>(table) == [WebUndefined undefined])
3825 table = nil;
3826 return [[NSBundle mainBundle] localizedStringForKey:key value:value table:table];
cb9c2100
JF
3827}
3828
3829@end
3830/* }}} */
6932575e 3831
7d6ecbef 3832/* Cydia Browser Controller {{{ */
ed349d9a 3833@interface CYBrowserController : BrowserController {
61b13cae
JF
3834 CydiaObject *cydia_;
3835}
e00439f7 3836
61b13cae
JF
3837@end
3838
9ae52960 3839@implementation CYBrowserController
61b13cae
JF
3840
3841- (void) dealloc {
3842 [cydia_ release];
3843 [super dealloc];
3844}
3845
5ec44e34
JF
3846- (void) setHeaders:(NSDictionary *)headers forHost:(NSString *)host {
3847}
3848
c2292b80
JF
3849- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3850 [super webView:view didClearWindowObject:window forFrame:frame];
5ec44e34
JF
3851
3852 WebDataSource *source([frame dataSource]);
3853 NSURLResponse *response([source response]);
3854 NSURL *url([response URL]);
3855 NSString *scheme([url scheme]);
3856
3857 NSHTTPURLResponse *http;
3858 if (scheme != nil && ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]))
3859 http = (NSHTTPURLResponse *) response;
3860 else
3861 http = nil;
3862
3863 NSDictionary *headers([http allHeaderFields]);
3864 NSString *host([url host]);
3865 [self setHeaders:headers forHost:host];
3866
daf7f6e2
JF
3867 if (
3868 [host isEqualToString:@"cydia.saurik.com"] ||
3869 [host hasSuffix:@".cydia.saurik.com"] ||
3870 [scheme isEqualToString:@"file"]
3871 )
37455cf8 3872 [window setValue:cydia_ forKey:@"cydia"];
61b13cae
JF
3873}
3874
017b2b71
JF
3875- (void) _setMoreHeaders:(NSMutableURLRequest *)request {
3876 if (System_ != NULL)
3877 [request setValue:System_ forHTTPHeaderField:@"X-System"];
61b13cae 3878 if (Machine_ != NULL)
017b2b71 3879 [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
5ec44e34
JF
3880 if (Token_ != nil)
3881 [request setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"];
61b13cae 3882 if (Role_ != nil)
017b2b71
JF
3883 [request setValue:Role_ forHTTPHeaderField:@"X-Role"];
3884}
61b13cae 3885
c2292b80
JF
3886- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
3887 NSMutableURLRequest *copy([[super webView:view resource:resource willSendRequest:request redirectResponse:response fromDataSource:source] mutableCopy]);
017b2b71 3888 [self _setMoreHeaders:copy];
61b13cae 3889 return copy;
e00439f7
JF
3890}
3891
daf7f6e2
JF
3892- (void) setDelegate:(id)delegate {
3893 [super setDelegate:delegate];
3894 [cydia_ setDelegate:delegate];
3895}
3896
9ae52960 3897- (id) init {
2938b930 3898 if ((self = [super initWithWidth:0 ofClass:[CYBrowserController class]]) != nil) {
61b13cae
JF
3899 cydia_ = [[CydiaObject alloc] initWithDelegate:indirect_];
3900
c2292b80 3901 WebView *webview([[webview_ _documentView] webView]);
61b13cae
JF
3902
3903 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
0a523b5a 3904
61b13cae
JF
3905 NSString *application = package == nil ? @"Cydia" : [NSString
3906 stringWithFormat:@"Cydia/%@",
3907 [package installed]
3908 ];
3909
61b13cae 3910 if (Safari_ != nil)
f26c1a7f 3911 application = [NSString stringWithFormat:@"Safari/%@ %@", Safari_, application];
0a523b5a 3912 if (Build_ != nil)
f26c1a7f 3913 application = [NSString stringWithFormat:@"Mobile/%@ %@", Build_, application];
0a523b5a 3914 if (Product_ != nil)
f26c1a7f 3915 application = [NSString stringWithFormat:@"Version/%@ %@", Product_, application];
61b13cae
JF
3916
3917 [webview setApplicationNameForUserAgent:application];
3918 } return self;
3919}
3920
3921@end
7d6ecbef 3922/* }}} */
61b13cae 3923
7d6ecbef 3924/* Confirmation {{{ */
f441e717 3925@protocol ConfirmationControllerDelegate
1ca35d78 3926- (void) cancelAndClear:(bool)clear;
9ae52960 3927- (void) confirmWithNavigationController:(UINavigationController *)navigation;
3ff1504e 3928- (void) queue;
e057ec05 3929@end
4941f41d 3930
f441e717 3931@interface ConfirmationController : CYBrowserController {
dbe0f181 3932 _transient Database *database_;
9c421310 3933 UIAlertView *essential_;
f464053e
JF
3934 NSArray *changes_;
3935 NSArray *issues_;
3936 NSArray *sizes_;
e00439f7 3937 BOOL substrate_;
e057ec05 3938}
a75f53e7 3939
9ae52960 3940- (id) initWithDatabase:(Database *)database;
686e302f 3941
a75f53e7
JF
3942@end
3943
f441e717 3944@implementation ConfirmationController
a75f53e7 3945
4941f41d 3946- (void) dealloc {
f464053e
JF
3947 [changes_ release];
3948 if (issues_ != nil)
3949 [issues_ release];
3950 [sizes_ release];
e057ec05
JF
3951 if (essential_ != nil)
3952 [essential_ release];
4941f41d
JF
3953 [super dealloc];
3954}
3955
9ae52960 3956- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
9c421310 3957 NSString *context([alert context]);
3319715b 3958
a5dd312c 3959 if ([context isEqualToString:@"remove"]) {
9ae52960
GP
3960 if (button == [alert cancelButtonIndex]) {
3961 [self dismissModalViewControllerAnimated:YES];
9c421310 3962 } else if (button == [alert firstOtherButtonIndex]) {
9ae52960
GP
3963 if (substrate_)
3964 Finish_ = 2;
3965 [delegate_ confirmWithNavigationController:[self navigationController]];
3319715b 3966 }
3319715b 3967
9c421310 3968 [alert dismissWithClickedButtonIndex:-1 animated:YES];
a5dd312c 3969 } else if ([context isEqualToString:@"unable"]) {
9ae52960 3970 [self dismissModalViewControllerAnimated:YES];
9c421310
GP
3971 [alert dismissWithClickedButtonIndex:-1 animated:YES];
3972 } else {
9ae52960
GP
3973 [super alertView:alert clickedButtonAtIndex:button];
3974 }
e057ec05
JF
3975}
3976
fa2cd4b3 3977- (void) _doContinue {
2064d251
GP
3978 [self dismissModalViewControllerAnimated:YES];
3979 [delegate_ cancelAndClear:NO];
fa2cd4b3 3980}
f99f86e2 3981
fa2cd4b3
JF
3982- (id) invokeDefaultMethodWithArguments:(NSArray *)args {
3983 [self performSelectorOnMainThread:@selector(_doContinue) withObject:nil waitUntilDone:NO];
2064d251
GP
3984 return nil;
3985}
3986
c2292b80
JF
3987- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
3988 [super webView:view didClearWindowObject:window forFrame:frame];
f464053e
JF
3989 [window setValue:changes_ forKey:@"changes"];
3990 [window setValue:issues_ forKey:@"issues"];
3991 [window setValue:sizes_ forKey:@"sizes"];
2064d251 3992 [window setValue:self forKey:@"queue"];
e057ec05
JF
3993}
3994
9ae52960
GP
3995- (id) initWithDatabase:(Database *)database {
3996 if ((self = [super init]) != nil) {
dbe0f181
JF
3997 database_ = database;
3998
9ae52960
GP
3999 [[self navigationItem] setTitle:UCLocalize("CONFIRM")];
4000
e057ec05
JF
4001 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
4002 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
4003 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
4004 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
4005 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
a75f53e7 4006
e057ec05 4007 bool remove(false);
a75f53e7 4008
e00439f7
JF
4009 pkgDepCache::Policy *policy([database_ policy]);
4010
e057ec05 4011 pkgCacheFile &cache([database_ cache]);
2a987aa5 4012 NSArray *packages = [database_ packages];
9fdd37d0 4013 for (Package *package in packages) {
2a987aa5 4014 pkgCache::PkgIterator iterator = [package iterator];
e057ec05 4015 pkgDepCache::StateCache &state(cache[iterator]);
a75f53e7 4016
2a987aa5
JF
4017 NSString *name([package name]);
4018
e057ec05
JF
4019 if (state.NewInstall())
4020 [installing addObject:name];
4021 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
4022 [reinstalling addObject:name];
4023 else if (state.Upgrade())
4024 [upgrading addObject:name];
4025 else if (state.Downgrade())
4026 [downgrading addObject:name];
4027 else if (state.Delete()) {
2a987aa5 4028 if ([package essential])
e057ec05
JF
4029 remove = true;
4030 [removing addObject:name];
e00439f7
JF
4031 } else continue;
4032
4033 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
4034 substrate_ |= DepSubstrate(iterator.CurrentVer());
e057ec05 4035 }
1cb11c5f 4036
e057ec05
JF
4037 if (!remove)
4038 essential_ = nil;
c59881cd 4039 else if (Advanced_) {
61b13cae 4040 NSString *parenthetical(UCLocalize("PARENTHETICAL"));
6932575e 4041
9c421310 4042 essential_ = [[UIAlertView alloc]
61b13cae 4043 initWithTitle:UCLocalize("REMOVING_ESSENTIALS")
9ae52960
GP
4044 message:UCLocalize("REMOVING_ESSENTIALS_EX")
4045 delegate:self
4046 cancelButtonTitle:[NSString stringWithFormat:parenthetical, UCLocalize("CANCEL_OPERATION"), UCLocalize("SAFE")]
4047 otherButtonTitles:[NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")], nil
4048 ];
d3bef7bc 4049
9c421310 4050 [essential_ setContext:@"remove"];
3319715b 4051 } else {
9c421310 4052 essential_ = [[UIAlertView alloc]
61b13cae 4053 initWithTitle:UCLocalize("UNABLE_TO_COMPLY")
9ae52960
GP
4054 message:UCLocalize("UNABLE_TO_COMPLY_EX")
4055 delegate:self
4056 cancelButtonTitle:UCLocalize("OKAY")
4057 otherButtonTitles:nil
e057ec05 4058 ];
1cb11c5f 4059
9ae52960 4060 [essential_ setContext:@"unable"];
e057ec05 4061 }
1cb11c5f 4062
f464053e
JF
4063 changes_ = [[NSArray alloc] initWithObjects:
4064 installing,
4065 reinstalling,
4066 upgrading,
4067 downgrading,
4068 removing,
4069 nil];
a75f53e7 4070
f464053e
JF
4071 issues_ = [database_ issues];
4072 if (issues_ != nil)
4073 issues_ = [issues_ retain];
a75f53e7 4074
f464053e
JF
4075 sizes_ = [[NSArray alloc] initWithObjects:
4076 SizeString([database_ fetcher].FetchNeeded()),
4077 SizeString([database_ fetcher].PartialPresent()),
f464053e 4078 nil];
a75f53e7 4079
f464053e 4080 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
d3bef7bc 4081
11f75d4f 4082 [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
c1edf105
GP
4083 initWithTitle:UCLocalize("CANCEL")
4084 // OLD: [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("CANCEL"), UCLocalize("QUEUE")]
9ae52960
GP
4085 style:UIBarButtonItemStylePlain
4086 target:self
4087 action:@selector(cancelButtonClicked)
11f75d4f 4088 ] autorelease]];
e057ec05 4089 } return self;
686e302f 4090}
20dd7407 4091
ed349d9a 4092- (void) applyRightButton {
9ae52960 4093#if !AlwaysReload && !IgnoreInstall
11f75d4f
JF
4094 if (issues_ == nil && ![self isLoading])
4095 [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
4096 initWithTitle:UCLocalize("CONFIRM")
4097 style:UIBarButtonItemStylePlain
4098 target:self
4099 action:@selector(confirmButtonClicked)
4100 ] autorelease]];
4101 else
4102 [super applyRightButton];
ed349d9a 4103#else
f99f86e2 4104 [[self navigationItem] setRightBarButtonItem:nil];
9ae52960 4105#endif
f464053e
JF
4106}
4107
9ae52960 4108- (void) cancelButtonClicked {
2064d251
GP
4109 [self dismissModalViewControllerAnimated:YES];
4110 [delegate_ cancelAndClear:YES];
f464053e
JF
4111}
4112
907a35d6 4113#if !AlwaysReload
9ae52960 4114- (void) confirmButtonClicked {
83105e6e 4115#if IgnoreInstall
9ae52960 4116 return;
83105e6e 4117#endif
f464053e 4118 if (essential_ != nil)
9ae52960 4119 [essential_ show];
f464053e
JF
4120 else {
4121 if (substrate_)
4122 Finish_ = 2;
9ae52960 4123 [delegate_ confirmWithNavigationController:[self navigationController]];
f464053e
JF
4124 }
4125}
907a35d6 4126#endif
f464053e 4127
e057ec05
JF
4128@end
4129/* }}} */
20dd7407 4130
e057ec05
JF
4131/* Progress Data {{{ */
4132@interface ProgressData : NSObject {
4133 SEL selector_;
a591888a
JF
4134 // XXX: should these really both be _transient?
4135 _transient id target_;
4136 _transient id object_;
a75f53e7
JF
4137}
4138
e057ec05 4139- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
686e302f 4140
e057ec05
JF
4141- (SEL) selector;
4142- (id) target;
4143- (id) object;
4144@end
686e302f 4145
e057ec05 4146@implementation ProgressData
686e302f 4147
e057ec05
JF
4148- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
4149 if ((self = [super init]) != nil) {
4150 selector_ = selector;
4151 target_ = target;
4152 object_ = object;
4153 } return self;
4154}
686e302f 4155
e057ec05
JF
4156- (SEL) selector {
4157 return selector_;
4158}
686e302f 4159
e057ec05
JF
4160- (id) target {
4161 return target_;
20dd7407
JF
4162}
4163
e057ec05
JF
4164- (id) object {
4165 return object_;
a75f53e7
JF
4166}
4167
4941f41d
JF
4168@end
4169/* }}} */
f441e717
GP
4170/* Progress Controller {{{ */
4171@interface ProgressController : CYViewController <
7600bd69 4172 ConfigurationDelegate,
e057ec05
JF
4173 ProgressDelegate
4174> {
7600bd69 4175 _transient Database *database_;
e057ec05
JF
4176 UIProgressBar *progress_;
4177 UITextView *output_;
4178 UITextLabel *status_;
238b07ce 4179 UIPushButton *close_;
b26eb97d 4180 BOOL running_;
1eb0c554 4181 SHA1SumValue springlist_;
bde2d79b 4182 SHA1SumValue notifyconf_;
6981ccdf 4183 NSString *title_;
4941f41d
JF
4184}
4185
9ae52960 4186- (id) initWithDatabase:(Database *)database delegate:(id)delegate;
4941f41d 4187
e057ec05
JF
4188- (void) _retachThread;
4189- (void) _detachNewThreadData:(ProgressData *)data;
4190- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
4941f41d 4191
b26eb97d
JF
4192- (BOOL) isRunning;
4193
4941f41d
JF
4194@end
4195
f441e717
GP
4196@protocol ProgressControllerDelegate
4197- (void) progressControllerIsComplete:(ProgressController *)sender;
e057ec05
JF
4198@end
4199
f441e717 4200@implementation ProgressController
4941f41d
JF
4201
4202- (void) dealloc {
9ae52960 4203 [database_ setDelegate:nil];
e057ec05
JF
4204 [progress_ release];
4205 [output_ release];
4206 [status_ release];
238b07ce 4207 [close_ release];
6981ccdf
JF
4208 if (title_ != nil)
4209 [title_ release];
4941f41d
JF
4210 [super dealloc];
4211}
4212
9ae52960
GP
4213- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
4214 if ((self = [super init]) != nil) {
7600bd69 4215 database_ = database;
9ae52960 4216 [database_ setDelegate:self];
e057ec05 4217 delegate_ = delegate;
1cb11c5f 4218
1e4922b8 4219 [[self view] setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]];
4941f41d 4220
9ae52960 4221 progress_ = [[UIProgressBar alloc] init];
d3bef7bc 4222 [progress_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)];
238b07ce 4223 [progress_ setStyle:0];
1cb11c5f 4224
9ae52960 4225 status_ = [[UITextLabel alloc] init];
d3bef7bc 4226 [status_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)];
87c76914
JF
4227 [status_ setColor:[UIColor whiteColor]];
4228 [status_ setBackgroundColor:[UIColor clearColor]];
e057ec05
JF
4229 [status_ setCentersHorizontally:YES];
4230 //[status_ setFont:font];
1cb11c5f 4231
9ae52960 4232 output_ = [[UITextView alloc] init];
3325a005 4233
d3bef7bc 4234 [output_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
e057ec05 4235 //[output_ setTextFont:@"Courier New"];
c59881cd 4236 [output_ setFont:[[output_ font] fontWithSize:12]];
87c76914
JF
4237 [output_ setTextColor:[UIColor whiteColor]];
4238 [output_ setBackgroundColor:[UIColor clearColor]];
e057ec05
JF
4239 [output_ setMarginTop:0];
4240 [output_ setAllowsRubberBanding:YES];
81ab76dc 4241 [output_ setEditable:NO];
9ae52960 4242 [[self view] addSubview:output_];
686e302f 4243
9ae52960 4244 close_ = [[UIPushButton alloc] init];
d3bef7bc 4245 [close_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)];
238b07ce
JF
4246 [close_ setAutosizesToFit:NO];
4247 [close_ setDrawsShadow:YES];
4248 [close_ setStretchBackground:YES];
238b07ce 4249 [close_ setEnabled:YES];
9ae52960 4250 [close_ setTitleFont:[UIFont boldSystemFontOfSize:22]];
017b2b71 4251 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:UIControlEventTouchUpInside];
238b07ce
JF
4252 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
4253 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
e057ec05 4254 } return self;
4941f41d
JF
4255}
4256
9ae52960
GP
4257- (void) positionViews {
4258 CGRect bounds = [[self view] bounds];
4259 CGSize prgsize = [UIProgressBar defaultSize];
4260
4261 CGRect prgrect = {{
4262 (bounds.size.width - prgsize.width) / 2,
12e0ef8f 4263 bounds.size.height - prgsize.height - 20
9ae52960 4264 }, prgsize};
f99f86e2 4265
96291f72 4266 float closewidth = std::min(bounds.size.width - 20, 300.0f);
9ae52960
GP
4267
4268 [progress_ setFrame:prgrect];
4269 [status_ setFrame:CGRectMake(
4270 10,
12e0ef8f 4271 bounds.size.height - prgsize.height - 50,
9ae52960
GP
4272 bounds.size.width - 20,
4273 24
4274 )];
4275 [output_ setFrame:CGRectMake(
4276 10,
4277 20,
4278 bounds.size.width - 20,
12e0ef8f 4279 bounds.size.height - 62
9ae52960
GP
4280 )];
4281 [close_ setFrame:CGRectMake(
4282 (bounds.size.width - closewidth) / 2,
12e0ef8f 4283 bounds.size.height - prgsize.height - 50,
9ae52960
GP
4284 closewidth,
4285 32 + prgsize.height
4286 )];
4287}
4288
4289- (void) viewWillAppear:(BOOL)animated {
4290 [super viewDidAppear:animated];
4291 [[self navigationItem] setHidesBackButton:YES];
1e4922b8 4292 [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack];
f99f86e2 4293
9ae52960 4294 [self positionViews];
4941f41d
JF
4295}
4296
9ae52960
GP
4297- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
4298 [self positionViews];
4941f41d
JF
4299}
4300
9ae52960 4301- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
3d257c70 4302 NSString *context([alert context]);
03d01f0e 4303
6981ccdf 4304 if ([context isEqualToString:@"conffile"]) {
9ae52960 4305 FILE *input = [database_ input];
96291f72
JF
4306 if (button == [alert cancelButtonIndex])
4307 fprintf(input, "N\n");
4308 else if (button == [alert firstOtherButtonIndex])
4309 fprintf(input, "Y\n");
9ae52960 4310 fflush(input);
6981ccdf
JF
4311 }
4312}
4313
4314- (void) closeButtonPushed {
4315 running_ = NO;
4316
864da84b
RP
4317 UpdateExternalStatus(0);
4318
6981ccdf
JF
4319 switch (Finish_) {
4320 case 0:
9ae52960 4321 [self dismissModalViewControllerAnimated:YES];
6981ccdf
JF
4322 break;
4323
4324 case 1:
74a834de
JF
4325 [delegate_ terminateWithSuccess];
4326 /*if ([delegate_ respondsToSelector:@selector(suspendWithAnimation:)])
4327 [delegate_ suspendWithAnimation:YES];
4328 else
4329 [delegate_ suspend];*/
6981ccdf
JF
4330 break;
4331
4332 case 2:
4333 system("launchctl stop com.apple.SpringBoard");
4334 break;
4335
4336 case 3:
4337 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
4338 break;
4339
4340 case 4:
4341 system("reboot");
4342 break;
03d01f0e 4343 }
6981ccdf 4344}
b26eb97d 4345
6981ccdf 4346- (void) _retachThread {
9ae52960 4347 [[self navigationItem] setTitle:UCLocalize("COMPLETE")];
6981ccdf 4348
9ae52960 4349 [[self view] addSubview:close_];
6981ccdf
JF
4350 [progress_ removeFromSuperview];
4351 [status_ removeFromSuperview];
4352
4353 [database_ popErrorWithTitle:title_];
f441e717 4354 [delegate_ progressControllerIsComplete:self];
cb9c2100 4355
bde2d79b 4356 if (Finish_ < 4) {
bd8e54e1
JF
4357 FileFd file;
4358 if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
4359 _error->Discard();
4360 else {
4361 MMap mmap(file, MMap::ReadOnly);
4362 SHA1Summation sha1;
4363 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
4364 if (!(notifyconf_ == sha1.Result()))
4365 Finish_ = 4;
4366 }
bde2d79b
JF
4367 }
4368
f464053e 4369 if (Finish_ < 3) {
bd8e54e1
JF
4370 FileFd file;
4371 if (!file.Open(SpringBoard_, FileFd::ReadOnly))
4372 _error->Discard();
4373 else {
4374 MMap mmap(file, MMap::ReadOnly);
4375 SHA1Summation sha1;
4376 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
4377 if (!(springlist_ == sha1.Result()))
4378 Finish_ = 3;
4379 }
1eb0c554
JF
4380 }
4381
03d01f0e 4382 switch (Finish_) {
9ae52960 4383 case 0: [close_ setTitle:UCLocalize("RETURN_TO_CYDIA")]; break; /* XXX: Maybe UCLocalize("DONE")? */
61b13cae
JF
4384 case 1: [close_ setTitle:UCLocalize("CLOSE_CYDIA")]; break;
4385 case 2: [close_ setTitle:UCLocalize("RESTART_SPRINGBOARD")]; break;
4386 case 3: [close_ setTitle:UCLocalize("RELOAD_SPRINGBOARD")]; break;
4387 case 4: [close_ setTitle:UCLocalize("REBOOT_DEVICE")]; break;
03d01f0e
JF
4388 }
4389
575ffd3c 4390 system("su -c /usr/bin/uicache mobile");
5f54c108 4391
864da84b
RP
4392 UpdateExternalStatus(Finish_ == 0 ? 2 : 0);
4393
b26eb97d 4394 [delegate_ setStatusBarShowsProgress:NO];
238b07ce
JF
4395}
4396
f464053e 4397- (void) _detachNewThreadData:(ProgressData *)data { _pooled
e057ec05 4398 [[data target] performSelector:[data selector] withObject:[data object]];
e057ec05 4399 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
a933cee2
JF
4400}
4401
e057ec05 4402- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
864da84b
RP
4403 UpdateExternalStatus(1);
4404
6981ccdf
JF
4405 if (title_ != nil)
4406 [title_ release];
4407 if (title == nil)
4408 title_ = nil;
4409 else
4410 title_ = [title retain];
4411
9ae52960 4412 [[self navigationItem] setTitle:title_];
686e302f 4413
e057ec05
JF
4414 [status_ setText:nil];
4415 [output_ setText:@""];
4416 [progress_ setProgress:0];
a933cee2 4417
238b07ce 4418 [close_ removeFromSuperview];
9ae52960
GP
4419 [[self view] addSubview:progress_];
4420 [[self view] addSubview:status_];
238b07ce 4421
b26eb97d
JF
4422 [delegate_ setStatusBarShowsProgress:YES];
4423 running_ = YES;
4424
bde2d79b 4425 {
bd8e54e1
JF
4426 FileFd file;
4427 if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
4428 _error->Discard();
4429 else {
4430 MMap mmap(file, MMap::ReadOnly);
4431 SHA1Summation sha1;
4432 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
4433 notifyconf_ = sha1.Result();
4434 }
bde2d79b
JF
4435 }
4436
f464053e 4437 {
bd8e54e1
JF
4438 FileFd file;
4439 if (!file.Open(SpringBoard_, FileFd::ReadOnly))
4440 _error->Discard();
4441 else {
4442 MMap mmap(file, MMap::ReadOnly);
4443 SHA1Summation sha1;
4444 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
4445 springlist_ = sha1.Result();
4446 }
1eb0c554
JF
4447 }
4448
e057ec05
JF
4449 [NSThread
4450 detachNewThreadSelector:@selector(_detachNewThreadData:)
4451 toTarget:self
a591888a 4452 withObject:[[[ProgressData alloc]
e057ec05
JF
4453 initWithSelector:selector
4454 target:target
4455 object:object
a591888a 4456 ] autorelease]
e057ec05 4457 ];
a933cee2
JF
4458}
4459
965edd52
JF
4460- (void) repairWithSelector:(SEL)selector {
4461 [self
4462 detachNewThreadSelector:selector
4463 toTarget:database_
4464 withObject:nil
61b13cae 4465 title:UCLocalize("REPAIRING")
965edd52
JF
4466 ];
4467}
4468
7600bd69
JF
4469- (void) setConfigurationData:(NSString *)data {
4470 [self
4471 performSelectorOnMainThread:@selector(_setConfigurationData:)
4472 withObject:data
4473 waitUntilDone:YES
4474 ];
4475}
4476
6981ccdf
JF
4477- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
4478 CYActionSheet *sheet([[[CYActionSheet alloc]
4479 initWithTitle:title
61b13cae 4480 buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil]
238b07ce 4481 defaultButtonIndex:0
6981ccdf 4482 ] autorelease]);
238b07ce 4483
9e639c5a 4484 [sheet setMessage:error];
6981ccdf
JF
4485 [sheet yieldToPopupAlertAnimated:YES];
4486 [sheet dismiss];
a933cee2
JF
4487}
4488
e057ec05
JF
4489- (void) setProgressTitle:(NSString *)title {
4490 [self
4491 performSelectorOnMainThread:@selector(_setProgressTitle:)
4492 withObject:title
4493 waitUntilDone:YES
4494 ];
a933cee2
JF
4495}
4496
e057ec05
JF
4497- (void) setProgressPercent:(float)percent {
4498 [self
4499 performSelectorOnMainThread:@selector(_setProgressPercent:)
4500 withObject:[NSNumber numberWithFloat:percent]
4501 waitUntilDone:YES
4502 ];
a933cee2
JF
4503}
4504
87c76914 4505- (void) startProgress {
87c76914
JF
4506}
4507
e057ec05
JF
4508- (void) addProgressOutput:(NSString *)output {
4509 [self
4510 performSelectorOnMainThread:@selector(_addProgressOutput:)
4511 withObject:output
4512 waitUntilDone:YES
4513 ];
a933cee2
JF
4514}
4515
87c76914 4516- (bool) isCancelling:(size_t)received {
87c76914
JF
4517 return false;
4518}
4519
7600bd69 4520- (void) _setConfigurationData:(NSString *)data {
faf4eb4f
JF
4521 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
4522
6981ccdf
JF
4523 if (!conffile_r(data)) {
4524 lprintf("E:invalid conffile\n");
4525 return;
4526 }
7600bd69
JF
4527
4528 NSString *ofile = conffile_r[1];
4529 //NSString *nfile = conffile_r[2];
4530
3d257c70 4531 UIAlertView *alert = [[[UIAlertView alloc]
61b13cae 4532 initWithTitle:UCLocalize("CONFIGURATION_UPGRADE")
9ae52960
GP
4533 message:[NSString stringWithFormat:@"%@\n\n%@", UCLocalize("CONFIGURATION_UPGRADE_EX"), ofile]
4534 delegate:self
4535 cancelButtonTitle:UCLocalize("KEEP_OLD_COPY")
4536 otherButtonTitles:UCLocalize("ACCEPT_NEW_COPY"),
3d257c70 4537 // XXX: UCLocalize("SEE_WHAT_CHANGED"),
9ae52960
GP
4538 nil
4539 ] autorelease];
3d257c70 4540
9ae52960 4541 [alert setContext:@"conffile"];
3d257c70 4542 [alert show];
7600bd69
JF
4543}
4544
e057ec05 4545- (void) _setProgressTitle:(NSString *)title {
907a35d6
JF
4546 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
4547 for (size_t i(0), e([words count]); i != e; ++i) {
4548 NSString *word([words objectAtIndex:i]);
4549 if (Package *package = [database_ packageWithName:word])
4550 [words replaceObjectAtIndex:i withObject:[package name]];
4551 }
4552
4553 [status_ setText:[words componentsJoinedByString:@" "]];
686e302f 4554}
a933cee2 4555
e057ec05
JF
4556- (void) _setProgressPercent:(NSNumber *)percent {
4557 [progress_ setProgress:[percent floatValue]];
242bcc6d
JF
4558}
4559
e057ec05
JF
4560- (void) _addProgressOutput:(NSString *)output {
4561 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
4562 CGSize size = [output_ contentSize];
4563 CGRect rect = {{0, size.height}, {size.width, 0}};
4564 [output_ scrollRectToVisible:rect animated:YES];
4565}
3957dd75 4566
b26eb97d 4567- (BOOL) isRunning {
5f54c108 4568 return running_;
b26eb97d
JF
4569}
4570
e057ec05
JF
4571@end
4572/* }}} */
3957dd75 4573
d4a9ec10 4574/* Cell Content View {{{ */
1e4922b8
JF
4575@protocol ContentDelegate
4576- (void) drawContentRect:(CGRect)rect;
4577@end
4578
5d8f1006 4579@interface ContentView : UIView {
1e4922b8 4580 _transient id<ContentDelegate> delegate_;
5d8f1006
JF
4581}
4582
4583@end
4584
d4a9ec10
GP
4585@implementation ContentView
4586- (id) initWithFrame:(CGRect)frame {
4587 if ((self = [super initWithFrame:frame]) != nil) {
4588 /* Fix landscape stretching. */
4589 [self setNeedsDisplayOnBoundsChange:YES];
4590 } return self;
4591}
4592
1e4922b8 4593- (void) setDelegate:(id<ContentDelegate>)delegate {
d4a9ec10
GP
4594 delegate_ = delegate;
4595}
4596
4597- (void) drawRect:(CGRect)rect {
4598 [super drawRect:rect];
4599 [delegate_ drawContentRect:rect];
4600}
4601@end
4602/* }}} */
4603/* Package Cell {{{ */
1e4922b8
JF
4604@interface PackageCell : UITableViewCell <
4605 ContentDelegate
4606> {
5e563e79
JF
4607 UIImage *icon_;
4608 NSString *name_;
4609 NSString *description_;
d8d9a65c 4610 bool commercial_;
5e563e79 4611 NSString *source_;
cb9c2100 4612 UIImage *badge_;
3ff1504e 4613 Package *package_;
5d8f1006
JF
4614 UIColor *color_;
4615 ContentView *content_;
4616 BOOL faded_;
4617 float fade_;
6981ccdf 4618 UIImage *placard_;
e057ec05 4619}
138ae18d 4620
e057ec05
JF
4621- (PackageCell *) init;
4622- (void) setPackage:(Package *)package;
1cb11c5f 4623
3319715b 4624+ (int) heightForPackage:(Package *)package;
5d8f1006
JF
4625- (void) drawContentRect:(CGRect)rect;
4626
4627@end
4628
e057ec05
JF
4629@implementation PackageCell
4630
5e563e79
JF
4631- (void) clearPackage {
4632 if (icon_ != nil) {
4633 [icon_ release];
4634 icon_ = nil;
4635 }
4636
4637 if (name_ != nil) {
4638 [name_ release];
4639 name_ = nil;
4640 }
4641
4642 if (description_ != nil) {
4643 [description_ release];
4644 description_ = nil;
4645 }
4646
4647 if (source_ != nil) {
4648 [source_ release];
4649 source_ = nil;
4650 }
cb9c2100
JF
4651
4652 if (badge_ != nil) {
4653 [badge_ release];
4654 badge_ = nil;
4655 }
3ff1504e 4656
6981ccdf
JF
4657 if (placard_ != nil) {
4658 [placard_ release];
4659 placard_ = nil;
4660 }
4661
3ff1504e
JF
4662 [package_ release];
4663 package_ = nil;
5e563e79
JF
4664}
4665
e057ec05 4666- (void) dealloc {
5e563e79 4667 [self clearPackage];
5d8f1006
JF
4668 [content_ release];
4669 [color_ release];
e057ec05 4670 [super dealloc];
686e302f 4671}
a933cee2 4672
5d8f1006
JF
4673- (float) fade {
4674 return faded_ ? [self selectionPercent] : fade_;
4675}
4676
e057ec05 4677- (PackageCell *) init {
5d8f1006
JF
4678 CGRect frame(CGRectMake(0, 0, 320, 74));
4679 if ((self = [super initWithFrame:frame reuseIdentifier:@"Package"]) != nil) {
4680 UIView *content([self contentView]);
4681 CGRect bounds([content bounds]);
d3bef7bc 4682
5d8f1006 4683 content_ = [[ContentView alloc] initWithFrame:bounds];
d3bef7bc
JF
4684 [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
4685 [content addSubview:content_];
4686
5d8f1006 4687 [content_ setDelegate:self];
5d8f1006 4688 [content_ setOpaque:YES];
5d8f1006
JF
4689 if ([self respondsToSelector:@selector(selectionPercent)])
4690 faded_ = YES;
e057ec05 4691 } return self;
686e302f 4692}
a933cee2 4693
5d8f1006
JF
4694- (void) _setBackgroundColor {
4695 UIColor *color;
4696 if (NSString *mode = [package_ mode]) {
4697 bool remove([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]);
4698 color = remove ? RemovingColor_ : InstallingColor_;
4699 } else
4700 color = [UIColor whiteColor];
4701
4702 [content_ setBackgroundColor:color];
4703 [self setNeedsDisplay];
4704}
4705
e057ec05 4706- (void) setPackage:(Package *)package {
d1494d2c 4707 [self clearPackage];
631a0a1e 4708 [package parse];
238b07ce 4709
e057ec05 4710 Source *source = [package source];
a933cee2 4711
dbe0f181 4712 icon_ = [[package icon] retain];
5e563e79 4713 name_ = [[package name] retain];
37455cf8
JF
4714
4715 if (IsWildcat_)
4716 description_ = [package longDescription];
4717 if (description_ == nil)
4718 description_ = [package shortDescription];
4719 if (description_ != nil)
4720 description_ = [description_ retain];
4721
d8d9a65c 4722 commercial_ = [package isCommercial];
e057ec05 4723
3ff1504e
JF
4724 package_ = [package retain];
4725
9e07091a
JF
4726 NSString *label = nil;
4727 bool trusted = false;
a933cee2 4728
e057ec05
JF
4729 if (source != nil) {
4730 label = [source label];
4731 trusted = [source trusted];
9e07091a 4732 } else if ([[package id] isEqualToString:@"firmware"])
61b13cae 4733 label = UCLocalize("APPLE");
faf4eb4f 4734 else
61b13cae 4735 label = [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("UNKNOWN"), UCLocalize("LOCAL")];
a933cee2 4736
6932575e 4737 NSString *from(label);
9e07091a 4738
6932575e
JF
4739 NSString *section = [package simpleSection];
4740 if (section != nil && ![section isEqualToString:label]) {
4741 section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"];
61b13cae 4742 from = [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), from, section];
6932575e 4743 }
9e07091a 4744
61b13cae 4745 from = [NSString stringWithFormat:UCLocalize("FROM"), from];
5e563e79 4746 source_ = [from retain];
e057ec05 4747
cb9c2100
JF
4748 if (NSString *purpose = [package primaryPurpose])
4749 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
4750 badge_ = [badge_ retain];
4751
6981ccdf
JF
4752 if ([package installed] != nil)
4753 if ((placard_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/installed.png", App_]]) != nil)
4754 placard_ = [placard_ retain];
4755
5d8f1006
JF
4756 [self _setBackgroundColor];
4757 [content_ setNeedsDisplay];
d8d9a65c
JF
4758}
4759
5d8f1006
JF
4760- (void) drawContentRect:(CGRect)rect {
4761 bool selected([self isSelected]);
37455cf8 4762 float width([self bounds].size.width);
3ff1504e 4763
5d8f1006
JF
4764#if 0
4765 CGContextRef context(UIGraphicsGetCurrentContext());
4766 [([[self selectedBackgroundView] superview] != nil ? [UIColor clearColor] : [self backgroundColor]) set];
4767 CGContextFillRect(context, rect);
4768#endif
a933cee2 4769
87c76914
JF
4770 if (icon_ != nil) {
4771 CGRect rect;
4772 rect.size = [icon_ size];
4773
4774 rect.size.width /= 2;
4775 rect.size.height /= 2;
4776
4777 rect.origin.x = 25 - rect.size.width / 2;
4778 rect.origin.y = 25 - rect.size.height / 2;
4779
4780 [icon_ drawInRect:rect];
4781 }
a933cee2 4782
cb9c2100
JF
4783 if (badge_ != nil) {
4784 CGSize size = [badge_ size];
4785
4786 [badge_ drawAtPoint:CGPointMake(
4787 36 - size.width / 2,
4788 36 - size.height / 2
4789 )];
4790 }
4791
5e563e79
JF
4792 if (selected)
4793 UISetColor(White_);
a933cee2 4794
5e563e79 4795 if (!selected)
d8d9a65c 4796 UISetColor(commercial_ ? Purple_ : Black_);
a6c1718e
DH
4797 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - (placard_ == nil ? 80 : 106)) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
4798 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
a933cee2 4799
5e563e79 4800 if (!selected)
d8d9a65c 4801 UISetColor(commercial_ ? Purplish_ : Gray_);
a6c1718e 4802 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ lineBreakMode:UILineBreakModeTailTruncation];
6981ccdf
JF
4803
4804 if (placard_ != nil)
5ec44e34 4805 [placard_ drawAtPoint:CGPointMake(width - 52, 9)];
1cb11c5f
JF
4806}
4807
5d8f1006
JF
4808- (void) setSelected:(BOOL)selected animated:(BOOL)fade {
4809 //[self _setBackgroundColor];
4810 [super setSelected:selected animated:fade];
4811 [content_ setNeedsDisplay];
3ff1504e
JF
4812}
4813
3319715b 4814+ (int) heightForPackage:(Package *)package {
631a0a1e 4815 return 73;
3319715b
JF
4816}
4817
20dd7407
JF
4818@end
4819/* }}} */
e057ec05 4820/* Section Cell {{{ */
1e4922b8
JF
4821@interface SectionCell : UITableViewCell <
4822 ContentDelegate
4823> {
4923fbd7 4824 NSString *basic_;
fa7bb92f 4825 NSString *section_;
5e563e79
JF
4826 NSString *name_;
4827 NSString *count_;
4828 UIImage *icon_;
d4a9ec10 4829 ContentView *content_;
18884e36 4830 UISwitch *switch_;
fa7bb92f 4831 BOOL editing_;
686e302f 4832}
a933cee2 4833
fa7bb92f 4834- (void) setSection:(Section *)section editing:(BOOL)editing;
e057ec05 4835
20dd7407
JF
4836@end
4837
e057ec05 4838@implementation SectionCell
20dd7407 4839
5e563e79 4840- (void) clearSection {
4923fbd7 4841 if (basic_ != nil) {
7805b429 4842 [basic_ release];
4923fbd7
JF
4843 basic_ = nil;
4844 }
4845
5e563e79 4846 if (section_ != nil) {
fa7bb92f 4847 [section_ release];
5e563e79
JF
4848 section_ = nil;
4849 }
faf4eb4f 4850
5e563e79
JF
4851 if (name_ != nil) {
4852 [name_ release];
4853 name_ = nil;
4854 }
faf4eb4f 4855
5e563e79
JF
4856 if (count_ != nil) {
4857 [count_ release];
4858 count_ = nil;
4859 }
faf4eb4f
JF
4860}
4861
5e563e79
JF
4862- (void) dealloc {
4863 [self clearSection];
4864 [icon_ release];
4865 [switch_ release];
d4a9ec10
GP
4866 [content_ release];
4867
5e563e79 4868 [super dealloc];
faf4eb4f
JF
4869}
4870
d4a9ec10
GP
4871- (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
4872 if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
5e563e79 4873 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
18884e36 4874 switch_ = [[UISwitch alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
d4a9ec10
GP
4875 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventValueChanged];
4876
4877 UIView *content([self contentView]);
4878 CGRect bounds([content bounds]);
4879
4880 content_ = [[ContentView alloc] initWithFrame:bounds];
4881 [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
4882 [content addSubview:content_];
4883 [content_ setBackgroundColor:[UIColor whiteColor]];
4884
4885 [content_ setDelegate:self];
686e302f 4886 } return self;
a933cee2
JF
4887}
4888
fa7bb92f 4889- (void) onSwitch:(id)sender {
4923fbd7 4890 NSMutableDictionary *metadata = [Sections_ objectForKey:basic_];
fa7bb92f
JF
4891 if (metadata == nil) {
4892 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
4923fbd7 4893 [Sections_ setObject:metadata forKey:basic_];
fa7bb92f
JF
4894 }
4895
4896 Changed_ = true;
d4a9ec10 4897 [metadata setObject:[NSNumber numberWithBool:([switch_ isOn] == NO)] forKey:@"Hidden"];
fa7bb92f
JF
4898}
4899
4900- (void) setSection:(Section *)section editing:(BOOL)editing {
4901 if (editing != editing_) {
4902 if (editing_)
4903 [switch_ removeFromSuperview];
4904 else
4905 [self addSubview:switch_];
4906 editing_ = editing;
4907 }
4908
5e563e79 4909 [self clearSection];
fa7bb92f 4910
e057ec05 4911 if (section == nil) {
61b13cae 4912 name_ = [UCLocalize("ALL_PACKAGES") retain];
5e563e79 4913 count_ = nil;
e057ec05 4914 } else {
7805b429 4915 basic_ = [section name];
4923fbd7
JF
4916 if (basic_ != nil)
4917 basic_ = [basic_ retain];
4918
43b742af 4919 section_ = [section localized];
fa7bb92f
JF
4920 if (section_ != nil)
4921 section_ = [section_ retain];
4923fbd7 4922
61b13cae 4923 name_ = [(section_ == nil || [section_ length] == 0 ? UCLocalize("NO_SECTION") : section_) retain];
5e563e79 4924 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
fa7bb92f
JF
4925
4926 if (editing_)
d4a9ec10 4927 [switch_ setOn:(isSectionVisible(basic_) ? 1 : 0) animated:NO];
e057ec05 4928 }
d4a9ec10 4929
1e4922b8 4930 [self setAccessoryType:editing ? UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator];
864db79b
JF
4931 [self setSelectionStyle:editing ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleBlue];
4932
d4a9ec10 4933 [content_ setNeedsDisplay];
5e563e79
JF
4934}
4935
daf7f6e2
JF
4936- (void) setFrame:(CGRect)frame {
4937 [super setFrame:frame];
d4a9ec10 4938
daf7f6e2
JF
4939 CGRect rect([switch_ frame]);
4940 [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)];
4941}
4942
d4a9ec10
GP
4943- (void) drawContentRect:(CGRect)rect {
4944 BOOL selected = [self isSelected];
f99f86e2 4945
5e563e79
JF
4946 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
4947
4948 if (selected)
4949 UISetColor(White_);
4950
4951 if (!selected)
4952 UISetColor(Black_);
575ffd3c 4953
d4a9ec10 4954 float width(rect.size.width);
575ffd3c 4955 if (editing_)
d4a9ec10 4956 width -= 87;
575ffd3c 4957
a6c1718e 4958 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(width - 70) withFont:Font22Bold_ lineBreakMode:UILineBreakModeTailTruncation];
fa7bb92f 4959
5e563e79
JF
4960 CGSize size = [count_ sizeWithFont:Font14_];
4961
4962 UISetColor(White_);
4963 if (count_ != nil)
cb9c2100 4964 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
a933cee2
JF
4965}
4966
e057ec05
JF
4967@end
4968/* }}} */
a933cee2 4969
59efd93a 4970/* File Table {{{ */
1e4922b8
JF
4971@interface FileTable : CYViewController <
4972 UITableViewDataSource,
4973 UITableViewDelegate
4974> {
e057ec05 4975 _transient Database *database_;
59efd93a
JF
4976 Package *package_;
4977 NSString *name_;
4978 NSMutableArray *files_;
d4a9ec10 4979 UITableView *list_;
59efd93a 4980}
a933cee2 4981
9ae52960 4982- (id) initWithDatabase:(Database *)database;
59efd93a
JF
4983- (void) setPackage:(Package *)package;
4984
4985@end
4986
4987@implementation FileTable
4988
4989- (void) dealloc {
4990 if (package_ != nil)
4991 [package_ release];
4992 if (name_ != nil)
4993 [name_ release];
4994 [files_ release];
4995 [list_ release];
4996 [super dealloc];
4997}
4998
9b62701b 4999- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
59efd93a
JF
5000 return files_ == nil ? 0 : [files_ count];
5001}
5002
2064d251
GP
5003/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
5004 return 24.0f;
5005}*/
59efd93a 5006
d4a9ec10 5007- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
9ae52960
GP
5008 static NSString *reuseIdentifier = @"Cell";
5009
d4a9ec10
GP
5010 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
5011 if (cell == nil) {
5012 cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
5013 [cell setFont:[UIFont systemFontOfSize:16]];
59efd93a 5014 }
d4a9ec10 5015 [cell setText:[files_ objectAtIndex:indexPath.row]];
1e4922b8 5016 [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
9ae52960 5017
d4a9ec10 5018 return cell;
59efd93a 5019}
a933cee2 5020
9ae52960
GP
5021- (id) initWithDatabase:(Database *)database {
5022 if ((self = [super init]) != nil) {
59efd93a 5023 database_ = database;
a933cee2 5024
9ae52960
GP
5025 [[self navigationItem] setTitle:UCLocalize("INSTALLED_FILES")];
5026
59efd93a 5027 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
20dd7407 5028
d4a9ec10 5029 list_ = [[UITableView alloc] initWithFrame:[[self view] bounds]];
2064d251
GP
5030 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
5031 [list_ setRowHeight:24.0f];
9ae52960 5032 [[self view] addSubview:list_];
59efd93a 5033
59efd93a 5034 [list_ setDataSource:self];
59efd93a 5035 [list_ setDelegate:self];
59efd93a
JF
5036 } return self;
5037}
5038
5039- (void) setPackage:(Package *)package {
5040 if (package_ != nil) {
5041 [package_ autorelease];
5042 package_ = nil;
5043 }
5044
5045 if (name_ != nil) {
5046 [name_ release];
5047 name_ = nil;
5048 }
5049
5050 [files_ removeAllObjects];
5051
5052 if (package != nil) {
5053 package_ = [package retain];
5054 name_ = [[package id] retain];
5055
f464053e
JF
5056 if (NSArray *files = [package files])
5057 [files_ addObjectsFromArray:files];
59efd93a 5058
fc19e583
JF
5059 if ([files_ count] != 0) {
5060 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
5061 [files_ removeObjectAtIndex:0];
9e07091a 5062 [files_ sortUsingSelector:@selector(compareByPath:)];
2a987aa5
JF
5063
5064 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
5065 [stack addObject:@"/"];
5066
5067 for (int i(0), e([files_ count]); i != e; ++i) {
5068 NSString *file = [files_ objectAtIndex:i];
5069 while (![file hasPrefix:[stack lastObject]])
5070 [stack removeLastObject];
5071 NSString *directory = [stack lastObject];
5072 [stack addObject:[file stringByAppendingString:@"/"]];
5073 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
9e07091a 5074 ([stack count] - 2) * 3, "",
2a987aa5
JF
5075 [file substringFromIndex:[directory length]]
5076 ]];
5077 }
59efd93a
JF
5078 }
5079 }
5080
5081 [list_ reloadData];
5082}
5083
59efd93a
JF
5084- (void) reloadData {
5085 [self setPackage:[database_ packageWithName:name_]];
59efd93a 5086}
686e302f 5087
686e302f 5088@end
e057ec05 5089/* }}} */
f441e717 5090/* Package Controller {{{ */
48861ec9
DH
5091@interface PackageController : CYBrowserController <
5092 UIActionSheetDelegate
5093> {
dbe0f181 5094 _transient Database *database_;
e057ec05
JF
5095 Package *package_;
5096 NSString *name_;
d8d9a65c 5097 bool commercial_;
965edd52 5098 NSMutableArray *buttons_;
c2292b80 5099 UIBarButtonItem *button_;
df5a7529
JF
5100}
5101
9ae52960 5102- (id) initWithDatabase:(Database *)database;
e057ec05 5103- (void) setPackage:(Package *)package;
686e302f 5104
e057ec05 5105@end
686e302f 5106
f441e717 5107@implementation PackageController
686e302f 5108
e057ec05 5109- (void) dealloc {
e057ec05
JF
5110 if (package_ != nil)
5111 [package_ release];
5112 if (name_ != nil)
5113 [name_ release];
c2292b80 5114
965edd52 5115 [buttons_ release];
c2292b80
JF
5116
5117 if (button_ != nil)
5118 [button_ release];
5119
e057ec05
JF
5120 [super dealloc];
5121}
20dd7407 5122
d287ae9a 5123- (void) release {
6932575e 5124 if ([self retainCount] == 1)
f441e717 5125 [delegate_ setPackageController:self];
6932575e 5126 [super release];
d287ae9a 5127}
6932575e
JF
5128
5129/* XXX: this is not safe at all... localization of /fail/ */
965edd52 5130- (void) _clickButtonWithName:(NSString *)name {
61b13cae 5131 if ([name isEqualToString:UCLocalize("CLEAR")])
3ff1504e 5132 [delegate_ clearPackage:package_];
61b13cae 5133 else if ([name isEqualToString:UCLocalize("INSTALL")])
965edd52 5134 [delegate_ installPackage:package_];
61b13cae 5135 else if ([name isEqualToString:UCLocalize("REINSTALL")])
965edd52 5136 [delegate_ installPackage:package_];
61b13cae 5137 else if ([name isEqualToString:UCLocalize("REMOVE")])
965edd52 5138 [delegate_ removePackage:package_];
61b13cae 5139 else if ([name isEqualToString:UCLocalize("UPGRADE")])
965edd52
JF
5140 [delegate_ installPackage:package_];
5141 else _assert(false);
5142}
5143
1ca35d78 5144- (void) actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)button {
a5dd312c 5145 NSString *context([sheet context]);
965edd52 5146
a5dd312c 5147 if ([context isEqualToString:@"modify"]) {
1ca35d78
GP
5148 if (button != [sheet cancelButtonIndex]) {
5149 NSString *buttonName = [buttons_ objectAtIndex:button];
5150 [self _clickButtonWithName:buttonName];
5151 }
f99f86e2 5152
1ca35d78 5153 [sheet dismissWithClickedButtonIndex:-1 animated:YES];
1ca35d78 5154 }
686e302f 5155}
4941f41d 5156
c2292b80
JF
5157- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
5158 [super webView:view didClearWindowObject:window forFrame:frame];
6932575e 5159 [window setValue:package_ forKey:@"package"];
ad554f10
JF
5160}
5161
7b00c562 5162- (bool) _allowJavaScriptPanel {
d8d9a65c 5163 return commercial_;
7b00c562
JF
5164}
5165
907a35d6 5166#if !AlwaysReload
48861ec9 5167- (void) _customButtonClicked {
6981ccdf
JF
5168 int count([buttons_ count]);
5169 if (count == 0)
5170 return;
4941f41d 5171
965edd52
JF
5172 if (count == 1)
5173 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
5174 else {
1ca35d78 5175 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:count];
965edd52 5176 [buttons addObjectsFromArray:buttons_];
e057ec05 5177
1ca35d78 5178 UIActionSheet *sheet = [[[UIActionSheet alloc]
fc19e583 5179 initWithTitle:nil
e057ec05 5180 delegate:self
1ca35d78
GP
5181 cancelButtonTitle:nil
5182 destructiveButtonTitle:nil
5183 otherButtonTitles:nil
5184 ] autorelease];
5185
5186 for (NSString *button in buttons) [sheet addButtonWithTitle:button];
5187 if (!IsWildcat_) {
5188 [sheet addButtonWithTitle:UCLocalize("CANCEL")];
5189 [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
5190 }
5191 [sheet setContext:@"modify"];
f99f86e2 5192
9ae52960 5193 [delegate_ showActionSheet:sheet fromItem:[[self navigationItem] rightBarButtonItem]];
e057ec05 5194 }
686e302f 5195}
1ce016d4 5196
3db9f43c
DH
5197// We don't want to allow non-commercial packages to do custom things to the install button,
5198// so it must call customButtonClicked with a custom commercial_ == 1 fallthrough.
48861ec9 5199- (void) customButtonClicked {
3db9f43c
DH
5200 if (commercial_)
5201 [super customButtonClicked];
5202 else
48861ec9 5203 [self _customButtonClicked];
1ce016d4 5204}
c8e3b4b3
GP
5205
5206- (void) reloadButtonClicked {
b36becfc
GP
5207 // Don't reload a package view by clicking the button.
5208}
5209
5210- (void) applyLoadingTitle {
5211 // Don't show "Loading" as the title. Ever.
c8e3b4b3 5212}
3db9f43c
DH
5213
5214- (UIBarButtonItem *) rightButton {
c2292b80 5215 return button_;
3db9f43c 5216}
907a35d6 5217#endif
4941f41d 5218
9ae52960
GP
5219- (id) initWithDatabase:(Database *)database {
5220 if ((self = [super init]) != nil) {
e057ec05 5221 database_ = database;
965edd52 5222 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
6932575e 5223 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
e057ec05 5224 } return self;
a75f53e7
JF
5225}
5226
e057ec05
JF
5227- (void) setPackage:(Package *)package {
5228 if (package_ != nil) {
5229 [package_ autorelease];
5230 package_ = nil;
5231 }
5232
5233 if (name_ != nil) {
5234 [name_ release];
5235 name_ = nil;
5236 }
5237
965edd52
JF
5238 [buttons_ removeAllObjects];
5239
e057ec05 5240 if (package != nil) {
631a0a1e
JF
5241 [package parse];
5242
e057ec05
JF
5243 package_ = [package retain];
5244 name_ = [[package id] retain];
d8d9a65c 5245 commercial_ = [package isCommercial];
e057ec05 5246
3ff1504e 5247 if ([package_ mode] != nil)
61b13cae 5248 [buttons_ addObject:UCLocalize("CLEAR")];
965edd52 5249 if ([package_ source] == nil);
238b07ce 5250 else if ([package_ upgradableAndEssential:NO])
61b13cae 5251 [buttons_ addObject:UCLocalize("UPGRADE")];
b4c4fac4 5252 else if ([package_ uninstalled])
61b13cae 5253 [buttons_ addObject:UCLocalize("INSTALL")];
965edd52 5254 else
61b13cae 5255 [buttons_ addObject:UCLocalize("REINSTALL")];
b4c4fac4 5256 if (![package_ uninstalled])
61b13cae 5257 [buttons_ addObject:UCLocalize("REMOVE")];
c2292b80 5258 }
6932575e 5259
c2292b80
JF
5260 if (button_ != nil)
5261 [button_ release];
6932575e 5262
c2292b80
JF
5263 NSString *title;
5264 switch ([buttons_ count]) {
5265 case 0: title = nil; break;
5266 case 1: title = [buttons_ objectAtIndex:0]; break;
5267 default: title = UCLocalize("MODIFY"); break;
e057ec05 5268 }
c2292b80
JF
5269
5270 button_ = [[UIBarButtonItem alloc]
5271 initWithTitle:title
5272 style:UIBarButtonItemStylePlain
5273 target:self
5274 action:@selector(customButtonClicked)
5275 ];
3a3ed4dc
JF
5276
5277 [self reloadURL];
9ae52960 5278}
6932575e 5279
d8d9a65c
JF
5280- (bool) isLoading {
5281 return commercial_ ? [super isLoading] : false;
a99d2808
JF
5282}
5283
e057ec05
JF
5284- (void) reloadData {
5285 [self setPackage:[database_ packageWithName:name_]];
b6ffa083
JF
5286}
5287
686e302f
JF
5288@end
5289/* }}} */
686e302f 5290/* Package Table {{{ */
1e4922b8
JF
5291@interface PackageTable : UIView <
5292 UITableViewDataSource,
5293 UITableViewDelegate
5294> {
e057ec05 5295 _transient Database *database_;
e057ec05 5296 NSMutableArray *packages_;
686e302f 5297 NSMutableArray *sections_;
5d8f1006
JF
5298 UITableView *list_;
5299 NSMutableArray *index_;
5300 NSMutableDictionary *indices_;
a591888a
JF
5301 // XXX: this target_ seems to be delegate_. :(
5302 _transient id target_;
9ae52960 5303 SEL action_;
a591888a
JF
5304 // XXX: why do we even have this delegate_?
5305 _transient id delegate_;
a75f53e7
JF
5306}
5307
9ae52960 5308- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action;
686e302f
JF
5309
5310- (void) setDelegate:(id)delegate;
8993ad57 5311
e057ec05 5312- (void) reloadData;
8993ad57 5313- (void) resetCursor;
686e302f 5314
5d8f1006 5315- (UITableView *) list;
2a987aa5 5316
59efd93a
JF
5317- (void) setShouldHideHeaderInShortLists:(BOOL)hide;
5318
9ae52960
GP
5319- (void) deselectWithAnimation:(BOOL)animated;
5320
686e302f
JF
5321@end
5322
5323@implementation PackageTable
5324
5325- (void) dealloc {
e057ec05 5326 [packages_ release];
686e302f 5327 [sections_ release];
e057ec05 5328 [list_ release];
5d8f1006
JF
5329 [index_ release];
5330 [indices_ release];
9ae52960 5331
686e302f 5332 [super dealloc];
b6ffa083
JF
5333}
5334
5d8f1006
JF
5335- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
5336 NSInteger count([sections_ count]);
5337 return count == 0 ? 1 : count;
686e302f 5338}
4941f41d 5339
5d8f1006
JF
5340- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
5341 if ([sections_ count] == 0)
5342 return nil;
686e302f
JF
5343 return [[sections_ objectAtIndex:section] name];
5344}
a75f53e7 5345
5d8f1006
JF
5346- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
5347 if ([sections_ count] == 0)
5348 return 0;
5349 return [[sections_ objectAtIndex:section] count];
686e302f 5350}
a75f53e7 5351
5d8f1006
JF
5352- (Package *) packageAtIndexPath:(NSIndexPath *)path {
5353 Section *section([sections_ objectAtIndex:[path section]]);
5354 NSInteger row([path row]);
5355 Package *package([packages_ objectAtIndex:([section row] + row)]);
5356 return package;
686e302f 5357}
a75f53e7 5358
5d8f1006 5359- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
1e4922b8 5360 PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
5d8f1006
JF
5361 if (cell == nil)
5362 cell = [[[PackageCell alloc] init] autorelease];
5363 [cell setPackage:[self packageAtIndexPath:path]];
5364 return cell;
686e302f 5365}
a75f53e7 5366
9ae52960
GP
5367- (void) deselectWithAnimation:(BOOL)animated {
5368 [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
5369}
5370
2064d251 5371/*- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path {
5d8f1006 5372 return [PackageCell heightForPackage:[self packageAtIndexPath:path]];
2064d251 5373}*/
a75f53e7 5374
5d8f1006
JF
5375- (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path {
5376 Package *package([self packageAtIndexPath:path]);
c3f582af 5377 package = [database_ packageWithName:[package id]];
9ae52960 5378 [target_ performSelector:action_ withObject:package];
5d8f1006
JF
5379 return path;
5380}
5381
5382- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView {
5383 return [packages_ count] > 20 ? index_ : nil;
5384}
5385
5386- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
5387 return index;
a75f53e7
JF
5388}
5389
9ae52960
GP
5390- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action {
5391 if ((self = [super initWithFrame:frame]) != nil) {
e057ec05 5392 database_ = database;
9ae52960
GP
5393
5394 target_ = target;
5395 action_ = action;
e057ec05 5396
5d8f1006
JF
5397 index_ = [[NSMutableArray alloc] initWithCapacity:32];
5398 indices_ = [[NSMutableDictionary alloc] initWithCapacity:32];
5399
e057ec05 5400 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
686e302f 5401 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
a75f53e7 5402
5d8f1006 5403 list_ = [[UITableView alloc] initWithFrame:[self bounds] style:UITableViewStylePlain];
d3bef7bc 5404 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
2064d251 5405 [list_ setRowHeight:73.0f];
d3bef7bc
JF
5406 [self addSubview:list_];
5407
686e302f 5408 [list_ setDataSource:self];
5d8f1006 5409 [list_ setDelegate:self];
686e302f 5410 } return self;
a75f53e7
JF
5411}
5412
5413- (void) setDelegate:(id)delegate {
4941f41d 5414 delegate_ = delegate;
686e302f
JF
5415}
5416
ce09fc27
JF
5417- (bool) hasPackage:(Package *)package {
5418 return true;
8993ad57
JF
5419}
5420
e057ec05
JF
5421- (void) reloadData {
5422 NSArray *packages = [database_ packages];
686e302f 5423
e057ec05 5424 [packages_ removeAllObjects];
686e302f
JF
5425 [sections_ removeAllObjects];
5426
3bddda52 5427 _profile(PackageTable$reloadData$Filter)
9fdd37d0 5428 for (Package *package in packages)
3bddda52
JF
5429 if ([self hasPackage:package])
5430 [packages_ addObject:package];
3bddda52 5431 _end
e057ec05 5432
5d8f1006
JF
5433 [index_ removeAllObjects];
5434 [indices_ removeAllObjects];
5435
686e302f
JF
5436 Section *section = nil;
5437
3bddda52 5438 _profile(PackageTable$reloadData$Section)
9fdd37d0
JF
5439 for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
5440 Package *package;
5441 unichar index;
5442
5443 _profile(PackageTable$reloadData$Section$Package)
5444 package = [packages_ objectAtIndex:offset];
5445 index = [package index];
5446 _end
686e302f 5447
3bddda52
JF
5448 if (section == nil || [section index] != index) {
5449 _profile(PackageTable$reloadData$Section$Allocate)
5450 section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
5451 _end
686e302f 5452
5d8f1006
JF
5453 [index_ addObject:[section name]];
5454 //[indices_ setObject:[NSNumber numberForInt:[sections_ count]] forKey:index];
5455
9fdd37d0
JF
5456 _profile(PackageTable$reloadData$Section$Add)
5457 [sections_ addObject:section];
5458 _end
3bddda52
JF
5459 }
5460
5461 [section addToCount];
5462 }
5463 _end
686e302f 5464
9fdd37d0
JF
5465 _profile(PackageTable$reloadData$List)
5466 [list_ reloadData];
5467 _end
686e302f
JF
5468}
5469
8993ad57 5470- (void) resetCursor {
5d8f1006 5471 [list_ scrollRectToVisible:CGRectMake(0, 0, 0, 0) animated:NO];
8993ad57
JF
5472}
5473
5d8f1006 5474- (UITableView *) list {
2a987aa5
JF
5475 return list_;
5476}
5477
59efd93a 5478- (void) setShouldHideHeaderInShortLists:(BOOL)hide {
5d8f1006 5479 //XXX:[list_ setShouldHideHeaderInShortLists:hide];
59efd93a
JF
5480}
5481
ce09fc27
JF
5482@end
5483/* }}} */
5484/* Filtered Package Table {{{ */
5485@interface FilteredPackageTable : PackageTable {
5486 SEL filter_;
142bd2db 5487 IMP imp_;
ce09fc27
JF
5488 id object_;
5489}
5490
5491- (void) setObject:(id)object;
5ec44e34 5492- (void) setObject:(id)object forFilter:(SEL)filter;
ce09fc27 5493
9ae52960 5494- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action filter:(SEL)filter with:(id)object;
ce09fc27
JF
5495
5496@end
5497
5498@implementation FilteredPackageTable
5499
5500- (void) dealloc {
5501 if (object_ != nil)
5502 [object_ release];
5503 [super dealloc];
5504}
5505
5ec44e34
JF
5506- (void) setFilter:(SEL)filter {
5507 filter_ = filter;
5508
5509 /* XXX: this is an unsafe optimization of doomy hell */
5510 Method method(class_getInstanceMethod([Package class], filter));
5511 _assert(method != NULL);
5512 imp_ = method_getImplementation(method);
5513 _assert(imp_ != NULL);
5514}
5515
ce09fc27
JF
5516- (void) setObject:(id)object {
5517 if (object_ != nil)
5518 [object_ release];
5519 if (object == nil)
5520 object_ = nil;
5521 else
5522 object_ = [object retain];
5523}
5524
5ec44e34
JF
5525- (void) setObject:(id)object forFilter:(SEL)filter {
5526 [self setFilter:filter];
5527 [self setObject:object];
5ec44e34
JF
5528}
5529
ce09fc27 5530- (bool) hasPackage:(Package *)package {
142bd2db
JF
5531 _profile(FilteredPackageTable$hasPackage)
5532 return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
5533 _end
ce09fc27
JF
5534}
5535
9ae52960
GP
5536- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action filter:(SEL)filter with:(id)object {
5537 if ((self = [super initWithFrame:frame database:database target:target action:action]) != nil) {
5ec44e34 5538 [self setFilter:filter];
9ae52960 5539 object_ = [object retain];
6e673d99 5540 [self reloadData];
ce09fc27
JF
5541 } return self;
5542}
5543
a75f53e7 5544@end
686e302f 5545/* }}} */
a75f53e7 5546
f441e717
GP
5547/* Filtered Package Controller {{{ */
5548@interface FilteredPackageController : CYViewController {
9ae52960
GP
5549 _transient Database *database_;
5550 FilteredPackageTable *packages_;
5551 NSString *title_;
5552}
5553
5554- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
5555
5556@end
f99f86e2 5557
f441e717 5558@implementation FilteredPackageController
9ae52960
GP
5559
5560- (void) dealloc {
5561 [packages_ release];
5562 [title_ release];
f99f86e2 5563
9ae52960
GP
5564 [super dealloc];
5565}
5566
5567- (void) viewDidAppear:(BOOL)animated {
5568 [super viewDidAppear:animated];
5569 [packages_ deselectWithAnimation:animated];
5570}
5571
5572- (void) didSelectPackage:(Package *)package {
f441e717 5573 PackageController *view([delegate_ packageController]);
9ae52960
GP
5574 [view setPackage:package];
5575 [view setDelegate:delegate_];
5576 [[self navigationController] pushViewController:view animated:YES];
5577}
5578
9b62701b 5579- (NSString *) title { return title_; }
9ae52960
GP
5580
5581- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
5582 if ((self = [super init]) != nil) {
5583 database_ = database;
5584 title_ = [title copy];
5585 [[self navigationItem] setTitle:title_];
5586
5587 packages_ = [[FilteredPackageTable alloc]
5588 initWithFrame:[[self view] bounds]
5589 database:database
5590 target:self
5591 action:@selector(didSelectPackage:)
5592 filter:filter
5593 with:object
5594 ];
5595
5596 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
5597 [[self view] addSubview:packages_];
5598 } return self;
5599}
5600
5601- (void) reloadData {
5602 [packages_ reloadData];
5603}
5604
5605- (void) setDelegate:(id)delegate {
5606 [super setDelegate:delegate];
5607 [packages_ setDelegate:delegate];
5608}
5609
5610@end
f99f86e2 5611
9ae52960
GP
5612/* }}} */
5613
f441e717
GP
5614/* Add Source Controller {{{ */
5615@interface AddSourceController : CYViewController {
faf4eb4f 5616 _transient Database *database_;
a75f53e7
JF
5617}
5618
9ae52960 5619- (id) initWithDatabase:(Database *)database;
686e302f 5620
faf4eb4f 5621@end
a75f53e7 5622
f441e717 5623@implementation AddSourceController
a75f53e7 5624
9ae52960
GP
5625- (id) initWithDatabase:(Database *)database {
5626 if ((self = [super init]) != nil) {
faf4eb4f
JF
5627 database_ = database;
5628 } return self;
e057ec05 5629}
a75f53e7 5630
faf4eb4f
JF
5631@end
5632/* }}} */
5633/* Source Cell {{{ */
1e4922b8
JF
5634@interface SourceCell : UITableViewCell <
5635 ContentDelegate
5636> {
5e563e79
JF
5637 UIImage *icon_;
5638 NSString *origin_;
5639 NSString *description_;
5640 NSString *label_;
d4a9ec10 5641 ContentView *content_;
686e302f
JF
5642}
5643
d4a9ec10 5644- (void) setSource:(Source *)source;
686e302f 5645
faf4eb4f 5646@end
686e302f 5647
faf4eb4f 5648@implementation SourceCell
686e302f 5649
d4a9ec10 5650- (void) clearSource {
5e563e79 5651 [icon_ release];
faf4eb4f
JF
5652 [origin_ release];
5653 [description_ release];
5654 [label_ release];
d4a9ec10
GP
5655
5656 icon_ = nil;
5657 origin_ = nil;
5658 description_ = nil;
5659 label_ = nil;
5660}
5661
5662- (void) setSource:(Source *)source {
5663 [self clearSource];
f99f86e2 5664
d4a9ec10
GP
5665 if (icon_ == nil)
5666 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
5667 if (icon_ == nil)
5668 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
5669 icon_ = [icon_ retain];
5670
5671 origin_ = [[source name] retain];
5672 label_ = [[source uri] retain];
5673 description_ = [[source description] retain];
5674
5675 [content_ setNeedsDisplay];
5676}
5677
5678- (void) dealloc {
5679 [self clearSource];
5680 [content_ release];
faf4eb4f 5681 [super dealloc];
e057ec05 5682}
686e302f 5683
d4a9ec10
GP
5684- (SourceCell *) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
5685 if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
5686 UIView *content([self contentView]);
5687 CGRect bounds([content bounds]);
5688
5689 content_ = [[ContentView alloc] initWithFrame:bounds];
5690 [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
5691 [content_ setBackgroundColor:[UIColor whiteColor]];
5692 [content addSubview:content_];
5693
5694 [content_ setDelegate:self];
5695 [content_ setOpaque:YES];
faf4eb4f 5696 } return self;
48c0461e
JF
5697}
5698
d4a9ec10
GP
5699- (void) setSelected:(BOOL)selected animated:(BOOL)animated {
5700 [super setSelected:selected animated:animated];
5701 [content_ setNeedsDisplay];
5702}
5703
5704- (void) drawContentRect:(CGRect)rect {
5705 bool selected([self isSelected]);
5ec44e34
JF
5706 float width(rect.size.width);
5707
5e563e79
JF
5708 if (icon_ != nil)
5709 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
faf4eb4f 5710
5e563e79
JF
5711 if (selected)
5712 UISetColor(White_);
faf4eb4f 5713
5e563e79
JF
5714 if (!selected)
5715 UISetColor(Black_);
a6c1718e 5716 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - 80) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
faf4eb4f 5717
5e563e79
JF
5718 if (!selected)
5719 UISetColor(Blue_);
a6c1718e 5720 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
faf4eb4f 5721
5e563e79
JF
5722 if (!selected)
5723 UISetColor(Gray_);
a6c1718e 5724 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 40) withFont:Font14_ lineBreakMode:UILineBreakModeTailTruncation];
faf4eb4f
JF
5725}
5726
5727@end
5728/* }}} */
5729/* Source Table {{{ */
1e4922b8
JF
5730@interface SourceTable : CYViewController <
5731 UITableViewDataSource,
5732 UITableViewDelegate
5733> {
faf4eb4f 5734 _transient Database *database_;
d4a9ec10 5735 UITableView *list_;
faf4eb4f 5736 NSMutableArray *sources_;
faf4eb4f
JF
5737 int offset_;
5738
5739 NSString *href_;
5740 UIProgressHUD *hud_;
5741 NSError *error_;
5742
5743 //NSURLConnection *installer_;
3d3f4666 5744 NSURLConnection *trivial_;
faf4eb4f
JF
5745 NSURLConnection *trivial_bz2_;
5746 NSURLConnection *trivial_gz_;
5747 //NSURLConnection *automatic_;
5748
3d3f4666 5749 BOOL cydia_;
faf4eb4f
JF
5750}
5751
9ae52960 5752- (id) initWithDatabase:(Database *)database;
faf4eb4f 5753
1e4922b8
JF
5754- (void) updateButtonsForEditingStatus:(BOOL)editing animated:(BOOL)animated;
5755
faf4eb4f
JF
5756@end
5757
5758@implementation SourceTable
5759
a591888a 5760- (void) _releaseConnection:(NSURLConnection *)connection {
faf4eb4f
JF
5761 if (connection != nil) {
5762 [connection cancel];
5763 //[connection setDelegate:nil];
5764 [connection release];
5765 }
5766}
5767
5768- (void) dealloc {
faf4eb4f
JF
5769 if (href_ != nil)
5770 [href_ release];
5771 if (hud_ != nil)
5772 [hud_ release];
5773 if (error_ != nil)
5774 [error_ release];
5775
a591888a
JF
5776 //[self _releaseConnection:installer_];
5777 [self _releaseConnection:trivial_];
5778 [self _releaseConnection:trivial_gz_];
5779 [self _releaseConnection:trivial_bz2_];
5780 //[self _releaseConnection:automatic_];
faf4eb4f
JF
5781
5782 [sources_ release];
5783 [list_ release];
5784 [super dealloc];
5785}
5786
9ae52960
GP
5787- (void) viewDidAppear:(BOOL)animated {
5788 [super viewDidAppear:animated];
5789 [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
5790}
5791
9b62701b 5792- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
faf4eb4f
JF
5793 return offset_ == 0 ? 1 : 2;
5794}
5795
9b62701b 5796- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
faf4eb4f 5797 switch (section + (offset_ == 0 ? 1 : 0)) {
61b13cae
JF
5798 case 0: return UCLocalize("ENTERED_BY_USER");
5799 case 1: return UCLocalize("INSTALLED_BY_PACKAGE");
faf4eb4f 5800
6981ccdf 5801 _nodefault
faf4eb4f
JF
5802 }
5803}
5804
9b62701b 5805- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
d4a9ec10
GP
5806 int count = [sources_ count];
5807 switch (section) {
5808 case 0: return (offset_ == 0 ? count : offset_);
5809 case 1: return count - offset_;
faf4eb4f 5810
9ae52960 5811 _nodefault
faf4eb4f
JF
5812 }
5813}
5814
d4a9ec10
GP
5815- (Source *) sourceAtIndexPath:(NSIndexPath *)indexPath {
5816 unsigned idx = 0;
5817 switch (indexPath.section) {
5818 case 0: idx = indexPath.row; break;
5819 case 1: idx = indexPath.row + offset_; break;
5820
9ae52960 5821 _nodefault
d4a9ec10
GP
5822 }
5823 return [sources_ objectAtIndex:idx];
faf4eb4f
JF
5824}
5825
d4a9ec10
GP
5826- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
5827 Source *source = [self sourceAtIndexPath:indexPath];
5e563e79 5828 return [source description] == nil ? 56 : 73;
faf4eb4f
JF
5829}
5830
d4a9ec10 5831- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
9ae52960 5832 static NSString *cellIdentifier = @"SourceCell";
faf4eb4f 5833
d4a9ec10 5834 SourceCell *cell = (SourceCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
9ae52960
GP
5835 if(cell == nil) cell = [[[SourceCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease];
5836 [cell setSource:[self sourceAtIndexPath:indexPath]];
f99f86e2 5837
d4a9ec10 5838 return cell;
faf4eb4f
JF
5839}
5840
1e4922b8
JF
5841- (UITableViewCellAccessoryType) tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
5842 return UITableViewCellAccessoryDisclosureIndicator;
faf4eb4f
JF
5843}
5844
d4a9ec10
GP
5845- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
5846 Source *source = [self sourceAtIndexPath:indexPath];
faf4eb4f 5847
f441e717 5848 FilteredPackageController *packages = [[[FilteredPackageController alloc]
9ae52960 5849 initWithDatabase:database_
faf4eb4f
JF
5850 title:[source label]
5851 filter:@selector(isVisibleInSource:)
5852 with:source
5853 ] autorelease];
5854
5855 [packages setDelegate:delegate_];
5856
9ae52960 5857 [[self navigationController] pushViewController:packages animated:YES];
faf4eb4f
JF
5858}
5859
d4a9ec10
GP
5860- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
5861 Source *source = [self sourceAtIndexPath:indexPath];
faf4eb4f
JF
5862 return [source record] != nil;
5863}
5864
d4a9ec10
GP
5865- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
5866 Source *source = [self sourceAtIndexPath:indexPath];
faf4eb4f
JF
5867 [Sources_ removeObjectForKey:[source key]];
5868 [delegate_ syncData];
5869}
5870
b4dff19a
JF
5871- (void) complete {
5872 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
5873 @"deb", @"Type",
5874 href_, @"URI",
5875 @"./", @"Distribution",
5876 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
5877
5878 [delegate_ syncData];
5879}
5880
5881- (NSString *) getWarning {
7b00c562
JF
5882 NSString *href(href_);
5883 NSRange colon([href rangeOfString:@"://"]);
5884 if (colon.location != NSNotFound)
5885 href = [href substringFromIndex:(colon.location + 3)];
5886 href = [href stringByAddingPercentEscapes];
6981ccdf 5887 href = [CydiaURL(@"api/repotag/") stringByAppendingString:href];
b4dff19a
JF
5888 href = [href stringByCachingURLWithCurrentCDN];
5889
5890 NSURL *url([NSURL URLWithString:href]);
5891
5892 NSStringEncoding encoding;
5893 NSError *error(nil);
5894
5895 if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
5896 return [warning length] == 0 ? nil : warning;
5897 return nil;
5898}
5899
faf4eb4f 5900- (void) _endConnection:(NSURLConnection *)connection {
a591888a
JF
5901 // XXX: the memory management in this method is horribly awkward
5902
faf4eb4f 5903 NSURLConnection **field = NULL;
3d3f4666
JF
5904 if (connection == trivial_)
5905 field = &trivial_;
5906 else if (connection == trivial_bz2_)
faf4eb4f
JF
5907 field = &trivial_bz2_;
5908 else if (connection == trivial_gz_)
5909 field = &trivial_gz_;
5910 _assert(field != NULL);
5911 [connection release];
5912 *field = nil;
5913
5914 if (
3d3f4666 5915 trivial_ == nil &&
faf4eb4f
JF
5916 trivial_bz2_ == nil &&
5917 trivial_gz_ == nil
5918 ) {
7b00c562 5919 bool defer(false);
faf4eb4f 5920
3d3f4666 5921 if (cydia_) {
b4dff19a 5922 if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
7b00c562
JF
5923 defer = true;
5924
1ba930a4 5925 UIAlertView *alert = [[[UIAlertView alloc]
61b13cae 5926 initWithTitle:UCLocalize("SOURCE_WARNING")
9ae52960
GP
5927 message:warning
5928 delegate:self
5929 cancelButtonTitle:UCLocalize("CANCEL")
1ba930a4 5930 otherButtonTitles:UCLocalize("ADD_ANYWAY"), nil
b4dff19a
JF
5931 ] autorelease];
5932
9ae52960 5933 [alert setContext:@"warning"];
1ba930a4
GP
5934 [alert setNumberOfRows:1];
5935 [alert show];
b4dff19a
JF
5936 } else
5937 [self complete];
faf4eb4f 5938 } else if (error_ != nil) {
1ba930a4 5939 UIAlertView *alert = [[[UIAlertView alloc]
61b13cae 5940 initWithTitle:UCLocalize("VERIFICATION_ERROR")
9ae52960
GP
5941 message:[error_ localizedDescription]
5942 delegate:self
5943 cancelButtonTitle:UCLocalize("OK")
1ba930a4 5944 otherButtonTitles:nil
faf4eb4f
JF
5945 ] autorelease];
5946
9ae52960 5947 [alert setContext:@"urlerror"];
1ba930a4 5948 [alert show];
faf4eb4f 5949 } else {
1ba930a4 5950 UIAlertView *alert = [[[UIAlertView alloc]
61b13cae 5951 initWithTitle:UCLocalize("NOT_REPOSITORY")
9ae52960
GP
5952 message:UCLocalize("NOT_REPOSITORY_EX")
5953 delegate:self
5954 cancelButtonTitle:UCLocalize("OK")
1ba930a4 5955 otherButtonTitles:nil
faf4eb4f
JF
5956 ] autorelease];
5957
9ae52960 5958 [alert setContext:@"trivial"];
1ba930a4 5959 [alert show];
faf4eb4f
JF
5960 }
5961
7b00c562
JF
5962 [delegate_ setStatusBarShowsProgress:NO];
5963 [delegate_ removeProgressHUD:hud_];
5964
5965 [hud_ autorelease];
5966 hud_ = nil;
5967
5968 if (!defer) {
5969 [href_ release];
5970 href_ = nil;
5971 }
faf4eb4f
JF
5972
5973 if (error_ != nil) {
5974 [error_ release];
5975 error_ = nil;
5976 }
5977 }
5978}
5979
5980- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
5981 switch ([response statusCode]) {
5982 case 200:
3d3f4666 5983 cydia_ = YES;
faf4eb4f
JF
5984 }
5985}
5986
5987- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
cb9c2100 5988 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
faf4eb4f
JF
5989 if (error_ != nil)
5990 error_ = [error retain];
5991 [self _endConnection:connection];
5992}
5993
5994- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
5995 [self _endConnection:connection];
5996}
5997
9b62701b 5998- (NSString *) title { return UCLocalize("SOURCES"); }
9ae52960 5999
faf4eb4f
JF
6000- (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
6001 NSMutableURLRequest *request = [NSMutableURLRequest
6002 requestWithURL:[NSURL URLWithString:href]
6003 cachePolicy:NSURLRequestUseProtocolCachePolicy
daf7f6e2 6004 timeoutInterval:120.0
faf4eb4f
JF
6005 ];
6006
6007 [request setHTTPMethod:method];
6008
6932575e
JF
6009 if (Machine_ != NULL)
6010 [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
daf7f6e2
JF
6011 if (UniqueID_ != nil)
6012 [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
6932575e
JF
6013 if (Role_ != nil)
6014 [request setValue:Role_ forHTTPHeaderField:@"X-Role"];
6015
faf4eb4f
JF
6016 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
6017}
6018
1ba930a4
GP
6019- (void)alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
6020 NSString *context([alert context]);
a5dd312c
JF
6021
6022 if ([context isEqualToString:@"source"]) {
faf4eb4f
JF
6023 switch (button) {
6024 case 1: {
1ba930a4 6025 NSString *href = [[alert textField] text];
faf4eb4f
JF
6026
6027 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
6028
6029 if (![href hasSuffix:@"/"])
6030 href_ = [href stringByAppendingString:@"/"];
6031 else
6032 href_ = href;
6033 href_ = [href_ retain];
6034
3d3f4666 6035 trivial_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages"] method:@"HEAD"] retain];
faf4eb4f
JF
6036 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
6037 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
6038 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
6039
3d3f4666 6040 cydia_ = false;
faf4eb4f 6041
a591888a 6042 // XXX: this is stupid
7398a389 6043 hud_ = [[delegate_ addProgressHUD] retain];
61b13cae 6044 [hud_ setText:UCLocalize("VERIFYING_URL")];
faf4eb4f
JF
6045 } break;
6046
1ba930a4 6047 case 0:
faf4eb4f
JF
6048 break;
6049
6981ccdf 6050 _nodefault
faf4eb4f
JF
6051 }
6052
1ba930a4 6053 [alert dismissWithClickedButtonIndex:-1 animated:YES];
b7adefda 6054 } else if ([context isEqualToString:@"trivial"])
1ba930a4 6055 [alert dismissWithClickedButtonIndex:-1 animated:YES];
b7adefda 6056 else if ([context isEqualToString:@"urlerror"])
1ba930a4 6057 [alert dismissWithClickedButtonIndex:-1 animated:YES];
b4dff19a
JF
6058 else if ([context isEqualToString:@"warning"]) {
6059 switch (button) {
6060 case 1:
6061 [self complete];
6062 break;
6063
1ba930a4 6064 case 0:
b4dff19a
JF
6065 break;
6066
6981ccdf 6067 _nodefault
b4dff19a
JF
6068 }
6069
7b00c562
JF
6070 [href_ release];
6071 href_ = nil;
6072
1ba930a4 6073 [alert dismissWithClickedButtonIndex:-1 animated:YES];
b4dff19a 6074 }
faf4eb4f
JF
6075}
6076
9ae52960
GP
6077- (id) initWithDatabase:(Database *)database {
6078 if ((self = [super init]) != nil) {
6079 [[self navigationItem] setTitle:UCLocalize("SOURCES")];
6080 [self updateButtonsForEditingStatus:NO animated:NO];
f99f86e2 6081
faf4eb4f
JF
6082 database_ = database;
6083 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
6084
9ae52960 6085 list_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain];
d3bef7bc 6086 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
9ae52960 6087 [[self view] addSubview:list_];
d3bef7bc 6088
faf4eb4f 6089 [list_ setDataSource:self];
d4a9ec10 6090 [list_ setDelegate:self];
faf4eb4f
JF
6091
6092 [self reloadData];
6093 } return self;
6094}
6095
6096- (void) reloadData {
6097 pkgSourceList list;
6981ccdf
JF
6098 if (!list.ReadMainList())
6099 return;
faf4eb4f
JF
6100
6101 [sources_ removeAllObjects];
6102 [sources_ addObjectsFromArray:[database_ sources]];
f159ecd4 6103 _trace();
faf4eb4f 6104 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
f159ecd4 6105 _trace();
faf4eb4f 6106
96291f72 6107 int count([sources_ count]);
d4a9ec10
GP
6108 offset_ = 0;
6109 for (int i = 0; i != count; i++) {
96291f72
JF
6110 if ([[sources_ objectAtIndex:i] record] == nil)
6111 break;
6112 offset_++;
faf4eb4f
JF
6113 }
6114
d4a9ec10 6115 [list_ setEditing:NO];
9ae52960 6116 [self updateButtonsForEditingStatus:NO animated:NO];
faf4eb4f
JF
6117 [list_ reloadData];
6118}
6119
9ae52960 6120- (void) addButtonClicked {
f441e717 6121 /*[book_ pushPage:[[[AddSourceController alloc]
faf4eb4f
JF
6122 initWithBook:book_
6123 database:database_
6124 ] autorelease]];*/
6125
1ba930a4 6126 UIAlertView *alert = [[[UIAlertView alloc]
61b13cae 6127 initWithTitle:UCLocalize("ENTER_APT_URL")
9ae52960
GP
6128 message:nil
6129 delegate:self
6130 cancelButtonTitle:UCLocalize("CANCEL")
6131 otherButtonTitles:UCLocalize("ADD_SOURCE"), nil
faf4eb4f
JF
6132 ] autorelease];
6133
9ae52960
GP
6134 [alert setContext:@"source"];
6135 [alert setTransform:CGAffineTransformTranslate([alert transform], 0.0, 100.0)];
a5dd312c 6136
1ba930a4
GP
6137 [alert setNumberOfRows:1];
6138 [alert addTextFieldWithValue:@"http://" label:@""];
faf4eb4f 6139
1ba930a4 6140 UITextInputTraits *traits = [[alert textField] textInputTraits];
b7adefda
JF
6141 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6142 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
a5dd312c 6143 [traits setKeyboardType:UIKeyboardTypeURL];
b7adefda
JF
6144 // XXX: UIReturnKeyDone
6145 [traits setReturnKeyType:UIReturnKeyNext];
faf4eb4f 6146
1ba930a4 6147 [alert show];
faf4eb4f
JF
6148}
6149
9ae52960 6150- (void) updateButtonsForEditingStatus:(BOOL)editing animated:(BOOL)animated {
11f75d4f 6151 [[self navigationItem] setLeftBarButtonItem:(editing ? [[[UIBarButtonItem alloc]
9ae52960
GP
6152 initWithTitle:UCLocalize("ADD")
6153 style:UIBarButtonItemStylePlain
6154 target:self
6155 action:@selector(addButtonClicked)
11f75d4f 6156 ] autorelease] : [[self navigationItem] backBarButtonItem]) animated:animated];
9ae52960 6157
11f75d4f
JF
6158 [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
6159 initWithTitle:(editing ? UCLocalize("DONE") : UCLocalize("EDIT"))
6160 style:(editing ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain)
9ae52960
GP
6161 target:self
6162 action:@selector(editButtonClicked)
11f75d4f 6163 ] autorelease] animated:animated];
f99f86e2 6164
11f75d4f
JF
6165 if (IsWildcat_ && !editing)
6166 [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
ca45de84
GP
6167 initWithTitle:UCLocalize("SETTINGS")
6168 style:UIBarButtonItemStylePlain
6169 target:self
6170 action:@selector(settingsButtonClicked)
11f75d4f 6171 ] autorelease]];
ca45de84
GP
6172}
6173
6174- (void) settingsButtonClicked {
6175 [delegate_ showSettings];
faf4eb4f
JF
6176}
6177
9ae52960
GP
6178- (void) editButtonClicked {
6179 [list_ setEditing:![list_ isEditing] animated:YES];
f99f86e2 6180
9ae52960 6181 [self updateButtonsForEditingStatus:[list_ isEditing] animated:YES];
faf4eb4f
JF
6182}
6183
6184@end
6185/* }}} */
6186
f441e717
GP
6187/* Installed Controller {{{ */
6188@interface InstalledController : FilteredPackageController {
5e563e79 6189 BOOL expert_;
faf4eb4f
JF
6190}
6191
9ae52960 6192- (id) initWithDatabase:(Database *)database;
faf4eb4f 6193
1e4922b8
JF
6194- (void) updateRoleButton;
6195- (void) queueStatusDidChange;
6196
faf4eb4f
JF
6197@end
6198
f441e717 6199@implementation InstalledController
faf4eb4f
JF
6200
6201- (void) dealloc {
faf4eb4f
JF
6202 [super dealloc];
6203}
6204
9b62701b 6205- (NSString *) title { return UCLocalize("INSTALLED"); }
87c76914 6206
9ae52960 6207- (id) initWithDatabase:(Database *)database {
1c220bde 6208 if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED") filter:@selector(isInstalledAndUnfiltered:) with:[NSNumber numberWithBool:YES]]) != nil) {
9ae52960
GP
6209 [self updateRoleButton];
6210 [self queueStatusDidChange];
faf4eb4f
JF
6211 } return self;
6212}
6213
9ae52960
GP
6214#if !AlwaysReload
6215- (void) queueButtonClicked {
6216 [delegate_ queue];
faf4eb4f 6217}
9ae52960 6218#endif
faf4eb4f 6219
9ae52960
GP
6220- (void) queueStatusDidChange {
6221#if !AlwaysReload
6222 if (IsWildcat_) {
11f75d4f
JF
6223 if (Queuing_) {
6224 [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
6225 initWithTitle:UCLocalize("QUEUE")
6226 style:UIBarButtonItemStyleDone
6227 target:self
6228 action:@selector(queueButtonClicked)
6229 ] autorelease]];
6230 } else {
6231 [[self navigationItem] setLeftBarButtonItem:nil];
6232 }
9ae52960
GP
6233 }
6234#endif
5e563e79
JF
6235}
6236
9ae52960
GP
6237- (void) reloadData {
6238 [packages_ reloadData];
faf4eb4f
JF
6239}
6240
9ae52960 6241- (void) updateRoleButton {
11f75d4f
JF
6242 if (Role_ != nil && ![Role_ isEqualToString:@"Developer"])
6243 [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
6244 initWithTitle:(expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE"))
6245 style:(expert_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain)
6246 target:self
6247 action:@selector(roleButtonClicked)
6248 ] autorelease]];
faf4eb4f
JF
6249}
6250
9ae52960
GP
6251- (void) roleButtonClicked {
6252 [packages_ setObject:[NSNumber numberWithBool:expert_]];
6253 [packages_ reloadData];
6254 expert_ = !expert_;
5e563e79 6255
9ae52960 6256 [self updateRoleButton];
5e563e79
JF
6257}
6258
faf4eb4f
JF
6259- (void) setDelegate:(id)delegate {
6260 [super setDelegate:delegate];
6261 [packages_ setDelegate:delegate];
6262}
6263
6264@end
6265/* }}} */
6266
f441e717
GP
6267/* Home Controller {{{ */
6268@interface HomeController : CYBrowserController {
faf4eb4f
JF
6269}
6270
6271@end
6272
f441e717 6273@implementation HomeController
faf4eb4f 6274
017b2b71
JF
6275- (void) _setMoreHeaders:(NSMutableURLRequest *)request {
6276 [super _setMoreHeaders:request];
ba87b4de 6277
017b2b71
JF
6278 if (ChipID_ != nil)
6279 [request setValue:ChipID_ forHTTPHeaderField:@"X-Chip-ID"];
5ec44e34
JF
6280 if (UniqueID_ != nil)
6281 [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
3074466a
JF
6282 if (PLMN_ != nil)
6283 [request setValue:PLMN_ forHTTPHeaderField:@"X-Carrier-ID"];
017b2b71
JF
6284}
6285
9ae52960 6286- (void) aboutButtonClicked {
ba87b4de
JF
6287 UIAlertView *alert([[[UIAlertView alloc] init] autorelease]);
6288
99dc9e91
JF
6289 [alert setTitle:UCLocalize("ABOUT_CYDIA")];
6290 [alert addButtonWithTitle:UCLocalize("CLOSE")];
6291 [alert setCancelButtonIndex:0];
faf4eb4f 6292
99dc9e91 6293 [alert setMessage:
daf7f6e2 6294 @"Copyright (C) 2008-2010\n"
faf4eb4f
JF
6295 "Jay Freeman (saurik)\n"
6296 "saurik@saurik.com\n"
991507f3 6297 "http://www.saurik.com/"
faf4eb4f
JF
6298 ];
6299
99dc9e91 6300 [alert show];
faf4eb4f
JF
6301}
6302
9ae52960
GP
6303- (void) viewWillAppear:(BOOL)animated {
6304 [super viewWillAppear:animated];
2ecf1e7f 6305 //[[self navigationController] setNavigationBarHidden:YES animated:animated];
9ae52960
GP
6306}
6307
6308- (void) viewWillDisappear:(BOOL)animated {
6309 [super viewWillDisappear:animated];
2ecf1e7f 6310 //[[self navigationController] setNavigationBarHidden:NO animated:animated];
9ae52960
GP
6311}
6312
6313- (id) init {
6314 if ((self = [super init]) != nil) {
ba87b4de 6315 [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
9ae52960
GP
6316 initWithTitle:UCLocalize("ABOUT")
6317 style:UIBarButtonItemStylePlain
6318 target:self
6319 action:@selector(aboutButtonClicked)
ba87b4de 6320 ] autorelease]];
9ae52960 6321 } return self;
faf4eb4f
JF
6322}
6323
6324@end
1bb0d66c 6325/* }}} */
f441e717
GP
6326/* Manage Controller {{{ */
6327@interface ManageController : CYBrowserController {
faf4eb4f
JF
6328}
6329
1e4922b8 6330- (void) queueStatusDidChange;
faf4eb4f
JF
6331@end
6332
f441e717 6333@implementation ManageController
faf4eb4f 6334
9ae52960
GP
6335- (id) init {
6336 if ((self = [super init]) != nil) {
6337 [[self navigationItem] setTitle:UCLocalize("MANAGE")];
f99f86e2 6338
11f75d4f 6339 [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
9ae52960
GP
6340 initWithTitle:UCLocalize("SETTINGS")
6341 style:UIBarButtonItemStylePlain
6342 target:self
6343 action:@selector(settingsButtonClicked)
11f75d4f 6344 ] autorelease]];
f99f86e2 6345
9ae52960
GP
6346 [self queueStatusDidChange];
6347 } return self;
faf4eb4f
JF
6348}
6349
9ae52960 6350- (void) settingsButtonClicked {
c1edf105 6351 [delegate_ showSettings];
faf4eb4f
JF
6352}
6353
541a556a 6354#if !AlwaysReload
9ae52960
GP
6355- (void) queueButtonClicked {
6356 [delegate_ queue];
3ff1504e
JF
6357}
6358
b38613d7
GP
6359- (void) applyLoadingTitle {
6360 // No "Loading" title.
6361}
6362
6363- (void) applyRightButton {
6364 // No right button.
3ff1504e 6365}
b38613d7 6366#endif
3ff1504e 6367
9ae52960
GP
6368- (void) queueStatusDidChange {
6369#if !AlwaysReload
6370 if (!IsWildcat_ && Queuing_) {
11f75d4f 6371 [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
9ae52960
GP
6372 initWithTitle:UCLocalize("QUEUE")
6373 style:UIBarButtonItemStyleDone
6374 target:self
6375 action:@selector(queueButtonClicked)
11f75d4f 6376 ] autorelease]];
9ae52960
GP
6377 } else {
6378 [[self navigationItem] setRightBarButtonItem:nil];
6379 }
541a556a 6380#endif
9ae52960 6381}
faf4eb4f 6382
d8d9a65c 6383- (bool) isLoading {
a99d2808
JF
6384 return false;
6385}
6386
faf4eb4f 6387@end
1bb0d66c 6388/* }}} */
faf4eb4f 6389
9ae52960
GP
6390/* Refresh Bar {{{ */
6391@interface RefreshBar : UINavigationBar {
faf4eb4f
JF
6392 UIProgressIndicator *indicator_;
6393 UITextLabel *prompt_;
6394 UIProgressBar *progress_;
87c76914 6395 UINavigationButton *cancel_;
30d83bfe
JF
6396}
6397
30d83bfe 6398@end
30d83bfe 6399
9ae52960 6400@implementation RefreshBar
5ec44e34 6401
a591888a
JF
6402- (void) dealloc {
6403 [indicator_ release];
6404 [prompt_ release];
6405 [progress_ release];
6406 [cancel_ release];
6407 [super dealloc];
6408}
6409
ba6cbb36
GP
6410- (void) positionViews {
6411 CGRect frame = [cancel_ frame];
6412 frame.origin.x = [self frame].size.width - frame.size.width - 5;
6413 frame.origin.y = ([self frame].size.height - frame.size.height) / 2;
6414 [cancel_ setFrame:frame];
f99f86e2 6415
ba6cbb36
GP
6416 CGSize prgsize = {75, 100};
6417 CGRect prgrect = {{
6418 [self frame].size.width - prgsize.width - 10,
6419 ([self frame].size.height - prgsize.height) / 2
6420 } , prgsize};
6421 [progress_ setFrame:prgrect];
f99f86e2 6422
ba6cbb36
GP
6423 CGSize indsize([UIProgressIndicator defaultSizeForStyle:[indicator_ activityIndicatorViewStyle]]);
6424 unsigned indoffset = ([self frame].size.height - indsize.height) / 2;
6425 CGRect indrect = {{indoffset, indoffset}, indsize};
6426 [indicator_ setFrame:indrect];
f99f86e2 6427
ba6cbb36
GP
6428 CGSize prmsize = {215, indsize.height + 4};
6429 CGRect prmrect = {{
6430 indoffset * 2 + indsize.width,
6431 unsigned([self frame].size.height - prmsize.height) / 2 - 1
6432 }, prmsize};
6433 [prompt_ setFrame:prmrect];
6434}
6435
6436- (void)setFrame:(CGRect)frame {
6437 [super setFrame:frame];
f99f86e2 6438
ba6cbb36
GP
6439 [self positionViews];
6440}
6441
9ae52960
GP
6442- (id) initWithFrame:(CGRect)frame delegate:(id)delegate {
6443 if ((self = [super initWithFrame:frame])) {
6444 [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
5ec44e34 6445
9ae52960 6446 [self setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
1e4922b8 6447 [self setBarStyle:UIBarStyleBlack];
5ec44e34 6448
1e4922b8
JF
6449 UIBarStyle barstyle([self _barStyle:NO]);
6450 bool ugly(barstyle == UIBarStyleDefault);
f159ecd4
JF
6451
6452 UIProgressIndicatorStyle style = ugly ?
6453 UIProgressIndicatorStyleMediumBrown :
6454 UIProgressIndicatorStyleMediumWhite;
6455
ba6cbb36 6456 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectZero];
f159ecd4 6457 [indicator_ setStyle:style];
ba6cbb36 6458 [indicator_ startAnimation];
9ae52960 6459 [self addSubview:indicator_];
f159ecd4 6460
ba6cbb36 6461 prompt_ = [[UITextLabel alloc] initWithFrame:CGRectZero];
f159ecd4
JF
6462 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
6463 [prompt_ setBackgroundColor:[UIColor clearColor]];
ba6cbb36 6464 [prompt_ setFont:[UIFont systemFontOfSize:15]];
9ae52960 6465 [self addSubview:prompt_];
f159ecd4 6466
ba6cbb36
GP
6467 progress_ = [[UIProgressBar alloc] initWithFrame:CGRectZero];
6468 [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin];
d3bef7bc 6469 [progress_ setStyle:0];
ba6cbb36 6470 [self addSubview:progress_];
f99f86e2 6471
61b13cae 6472 cancel_ = [[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted];
e7a88a8c 6473 [cancel_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
9ae52960 6474 [cancel_ addTarget:delegate action:@selector(cancelPressed) forControlEvents:UIControlEventTouchUpInside];
ba6cbb36 6475 [cancel_ setBarStyle:barstyle];
f99f86e2 6476
ba6cbb36 6477 [self positionViews];
f159ecd4
JF
6478 } return self;
6479}
6480
9ae52960 6481- (void) cancel {
f159ecd4
JF
6482 [cancel_ removeFromSuperview];
6483}
6484
9ae52960
GP
6485- (void) start {
6486 [prompt_ setText:UCLocalize("UPDATING_DATABASE")];
6487 [progress_ setProgress:0];
6488 [self addSubview:cancel_];
6489}
6490
6491- (void) stop {
6492 [cancel_ removeFromSuperview];
6493}
6494
6495- (void) setPrompt:(NSString *)prompt {
6496 [prompt_ setText:prompt];
6497}
6498
6499- (void) setProgress:(float)progress {
6500 [progress_ setProgress:progress];
6501}
6502
6503@end
6504/* }}} */
6505
1e4922b8
JF
6506@class CYNavigationController;
6507
9ae52960 6508/* Cydia Tab Bar Controller {{{ */
e7a88a8c 6509@interface CYTabBarController : UITabBarController {
a591888a 6510 _transient Database *database_;
9ae52960
GP
6511}
6512
f99f86e2 6513@end
9ae52960
GP
6514
6515@implementation CYTabBarController
6516
e7a88a8c
GP
6517/* XXX: some logic should probably go here related to
6518freeing the view controllers on tab change */
f159ecd4 6519
9ae52960
GP
6520- (void) reloadData {
6521 size_t count([[self viewControllers] count]);
6522 for (size_t i(0); i != count; ++i) {
1e4922b8 6523 CYNavigationController *page([[self viewControllers] objectAtIndex:(count - i - 1)]);
9ae52960
GP
6524 [page reloadData];
6525 }
6526}
6527
982de8f1 6528- (id) initWithDatabase:(Database *)database {
9ae52960
GP
6529 if ((self = [super init]) != nil) {
6530 database_ = database;
9ae52960
GP
6531 } return self;
6532}
6533
6534@end
6535/* }}} */
6536
6537/* Cydia Navigation Controller {{{ */
1e4922b8 6538@interface CYNavigationController : UINavigationController {
9ae52960 6539 _transient Database *database_;
a591888a 6540 _transient id<UINavigationControllerDelegate> delegate_;
9ae52960
GP
6541}
6542
6543- (id) initWithDatabase:(Database *)database;
6544- (void) reloadData;
6545
6546@end
6547
6548
6549@implementation CYNavigationController
6550
15b41c77
GP
6551- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
6552 // Inherit autorotation settings for modal parents.
6553 if ([self parentViewController] && [[self parentViewController] modalViewController] == self) {
6554 return [[self parentViewController] shouldAutorotateToInterfaceOrientation:orientation];
6555 } else {
6556 return [super shouldAutorotateToInterfaceOrientation:orientation];
6557 }
6558}
6559
9ae52960
GP
6560- (void) dealloc {
6561 [super dealloc];
6562}
6563
6564- (void) reloadData {
6565 size_t count([[self viewControllers] count]);
6566 for (size_t i(0); i != count; ++i) {
1e4922b8 6567 CYViewController *page([[self viewControllers] objectAtIndex:(count - i - 1)]);
9ae52960
GP
6568 [page reloadData];
6569 }
6570}
6571
9b62701b 6572- (void) setDelegate:(id<UINavigationControllerDelegate>)delegate {
9ae52960
GP
6573 delegate_ = delegate;
6574}
6575
6576- (id) initWithDatabase:(Database *)database {
6577 if ((self = [super init]) != nil) {
6578 database_ = database;
6579 } return self;
6580}
6581
f159ecd4
JF
6582@end
6583/* }}} */
6584/* Cydia:// Protocol {{{ */
6585@interface CydiaURLProtocol : NSURLProtocol {
6586}
6587
6588@end
6589
6590@implementation CydiaURLProtocol
6591
6592+ (BOOL) canInitWithRequest:(NSURLRequest *)request {
6593 NSURL *url([request URL]);
6594 if (url == nil)
6595 return NO;
6596 NSString *scheme([[url scheme] lowercaseString]);
6597 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
6598 return NO;
6599 return YES;
6600}
6601
6602+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
6603 return request;
6604}
6605
a99d2808
JF
6606- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
6607 id<NSURLProtocolClient> client([self client]);
6e673d99
JF
6608 if (icon == nil)
6609 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
6610 else {
6611 NSData *data(UIImagePNGRepresentation(icon));
a99d2808 6612
6e673d99
JF
6613 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
6614 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
6615 [client URLProtocol:self didLoadData:data];
6616 [client URLProtocolDidFinishLoading:self];
6617 }
a99d2808
JF
6618}
6619
f159ecd4
JF
6620- (void) startLoading {
6621 id<NSURLProtocolClient> client([self client]);
6622 NSURLRequest *request([self request]);
6623
6624 NSURL *url([request URL]);
6625 NSString *href([url absoluteString]);
6626
6627 NSString *path([href substringFromIndex:8]);
6628 NSRange slash([path rangeOfString:@"/"]);
6629
6630 NSString *command;
6631 if (slash.location == NSNotFound) {
6632 command = path;
6633 path = nil;
6634 } else {
6635 command = [path substringToIndex:slash.location];
6636 path = [path substringFromIndex:(slash.location + 1)];
6637 }
6638
6639 Database *database([Database sharedInstance]);
6640
6641 if ([command isEqualToString:@"package-icon"]) {
6642 if (path == nil)
6643 goto fail;
6b4b3bee 6644 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
f159ecd4
JF
6645 Package *package([database packageWithName:path]);
6646 if (package == nil)
6647 goto fail;
6b4b3bee 6648 UIImage *icon([package icon]);
a99d2808 6649 [self _returnPNGWithImage:icon forRequest:request];
18159e09
JF
6650 } else if ([command isEqualToString:@"source-icon"]) {
6651 if (path == nil)
6652 goto fail;
6653 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
6654 NSString *source(Simplify(path));
18159e09
JF
6655 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
6656 if (icon == nil)
6657 icon = [UIImage applicationImageNamed:@"unknown.png"];
a99d2808
JF
6658 [self _returnPNGWithImage:icon forRequest:request];
6659 } else if ([command isEqualToString:@"uikit-image"]) {
6660 if (path == nil)
6661 goto fail;
6662 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
6663 UIImage *icon(_UIImageWithName(path));
6664 [self _returnPNGWithImage:icon forRequest:request];
6b4b3bee
JF
6665 } else if ([command isEqualToString:@"section-icon"]) {
6666 if (path == nil)
6667 goto fail;
6668 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
6669 NSString *section(Simplify(path));
6b4b3bee
JF
6670 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
6671 if (icon == nil)
6672 icon = [UIImage applicationImageNamed:@"unknown.png"];
a99d2808 6673 [self _returnPNGWithImage:icon forRequest:request];
f159ecd4
JF
6674 } else fail: {
6675 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
6676 }
6677}
6678
6679- (void) stopLoading {
6680}
6681
6682@end
6683/* }}} */
6684
f441e717 6685/* Sections Controller {{{ */
1e4922b8
JF
6686@interface SectionsController : CYViewController <
6687 UITableViewDataSource,
6688 UITableViewDelegate
6689> {
f159ecd4
JF
6690 _transient Database *database_;
6691 NSMutableArray *sections_;
6692 NSMutableArray *filtered_;
d4a9ec10 6693 UITableView *list_;
f159ecd4
JF
6694 UIView *accessory_;
6695 BOOL editing_;
6696}
6697
9ae52960 6698- (id) initWithDatabase:(Database *)database;
f159ecd4
JF
6699- (void) reloadData;
6700- (void) resetView;
6701
1e4922b8
JF
6702- (void) editButtonClicked;
6703
f159ecd4
JF
6704@end
6705
f441e717 6706@implementation SectionsController
f159ecd4
JF
6707
6708- (void) dealloc {
6709 [list_ setDataSource:nil];
6710 [list_ setDelegate:nil];
6711
6712 [sections_ release];
6713 [filtered_ release];
f159ecd4
JF
6714 [list_ release];
6715 [accessory_ release];
6716 [super dealloc];
6717}
6718
9ae52960
GP
6719- (void) viewDidAppear:(BOOL)animated {
6720 [super viewDidAppear:animated];
6721 [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
6722}
6723
d4a9ec10
GP
6724- (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath {
6725 Section *section = (editing_ ? [sections_ objectAtIndex:[indexPath row]] : ([indexPath row] == 0 ? nil : [filtered_ objectAtIndex:([indexPath row] - 1)]));
6726 return section;
f159ecd4
JF
6727}
6728
9b62701b 6729- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
d4a9ec10 6730 return editing_ ? [sections_ count] : [filtered_ count] + 1;
f159ecd4
JF
6731}
6732
2064d251
GP
6733/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
6734 return 45.0f;
6735}*/
f159ecd4 6736
d4a9ec10 6737- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
9ae52960
GP
6738 static NSString *reuseIdentifier = @"SectionCell";
6739
6740 SectionCell *cell = (SectionCell *) [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
96291f72
JF
6741 if (cell == nil)
6742 cell = [[[SectionCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
6743
d4a9ec10 6744 [cell setSection:[self sectionAtIndexPath:indexPath] editing:editing_];
f159ecd4 6745
d4a9ec10 6746 return cell;
f159ecd4
JF
6747}
6748
d4a9ec10 6749- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
864db79b
JF
6750 if (editing_)
6751 return;
d1e1aaab 6752
864db79b 6753 Section *section = [self sectionAtIndexPath:indexPath];
d4a9ec10 6754 NSString *name = [section name];
f159ecd4
JF
6755 NSString *title;
6756
d4a9ec10 6757 if ([indexPath row] == 0) {
f159ecd4
JF
6758 section = nil;
6759 name = nil;
61b13cae 6760 title = UCLocalize("ALL_PACKAGES");
f159ecd4 6761 } else {
ca06bb0e
JF
6762 if (name != nil) {
6763 name = [NSString stringWithString:name];
6932575e 6764 title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"];
ca06bb0e 6765 } else {
f159ecd4 6766 name = @"";
61b13cae 6767 title = UCLocalize("NO_SECTION");
f159ecd4
JF
6768 }
6769 }
6770
f441e717 6771 FilteredPackageController *table = [[[FilteredPackageController alloc]
9ae52960 6772 initWithDatabase:database_
f159ecd4 6773 title:title
6981ccdf 6774 filter:@selector(isVisibleInSection:)
f159ecd4
JF
6775 with:name
6776 ] autorelease];
6777
6778 [table setDelegate:delegate_];
6779
9ae52960 6780 [[self navigationController] pushViewController:table animated:YES];
f159ecd4
JF
6781}
6782
9b62701b 6783- (NSString *) title { return UCLocalize("SECTIONS"); }
9ae52960
GP
6784
6785- (id) initWithDatabase:(Database *)database {
6786 if ((self = [super init]) != nil) {
f159ecd4
JF
6787 database_ = database;
6788
9ae52960
GP
6789 [[self navigationItem] setTitle:UCLocalize("SECTIONS")];
6790
f159ecd4
JF
6791 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6792 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
6793
9ae52960 6794 list_ = [[UITableView alloc] initWithFrame:[[self view] bounds]];
d3bef7bc 6795 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
2064d251 6796 [list_ setRowHeight:45.0f];
9ae52960 6797 [[self view] addSubview:list_];
f159ecd4 6798
d4a9ec10 6799 [list_ setDataSource:self];
9ae52960 6800 [list_ setDelegate:self];
f159ecd4
JF
6801
6802 [self reloadData];
f159ecd4
JF
6803 } return self;
6804}
6805
6806- (void) reloadData {
6807 NSArray *packages = [database_ packages];
6808
6809 [sections_ removeAllObjects];
6810 [filtered_ removeAllObjects];
6811
6932575e 6812 NSMutableDictionary *sections([NSMutableDictionary dictionaryWithCapacity:32]);
f159ecd4
JF
6813
6814 _trace();
9fdd37d0 6815 for (Package *package in packages) {
f159ecd4 6816 NSString *name([package section]);
6932575e 6817 NSString *key(name == nil ? @"" : name);
f159ecd4 6818
6932575e
JF
6819 Section *section;
6820
6821 _profile(SectionsView$reloadData$Section)
6822 section = [sections objectForKey:key];
f159ecd4 6823 if (section == nil) {
6932575e 6824 _profile(SectionsView$reloadData$Section$Allocate)
9ee296df 6825 section = [[[Section alloc] initWithName:name localize:YES] autorelease];
6932575e
JF
6826 [sections setObject:section forKey:key];
6827 _end
f159ecd4 6828 }
6932575e
JF
6829 _end
6830
6831 [section addToCount];
fa7bb92f 6832
6932575e 6833 _profile(SectionsView$reloadData$Filter)
6981ccdf 6834 if (![package valid] || ![package visible])
6932575e
JF
6835 continue;
6836 _end
6837
6838 [section addToRow];
686e302f 6839 }
f159ecd4 6840 _trace();
a75f53e7 6841
fa7bb92f 6842 [sections_ addObjectsFromArray:[sections allValues]];
fa7bb92f 6843
43b742af 6844 [sections_ sortUsingSelector:@selector(compareByLocalized:)];
a75f53e7 6845
6932575e
JF
6846 for (Section *section in sections_) {
6847 size_t count([section row]);
43b742af 6848 if (count == 0)
6932575e 6849 continue;
a75f53e7 6850
9ee296df 6851 section = [[[Section alloc] initWithName:[section name] localized:[section localized]] autorelease];
6932575e
JF
6852 [section setCount:count];
6853 [filtered_ addObject:section];
686e302f 6854 }
4941f41d 6855
11f75d4f
JF
6856 [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
6857 initWithTitle:([sections_ count] == 0 ? nil : UCLocalize("EDIT"))
9ae52960
GP
6858 style:UIBarButtonItemStylePlain
6859 target:self
6860 action:@selector(editButtonClicked)
11f75d4f 6861 ] autorelease] animated:([[self navigationItem] rightBarButtonItem] != nil)];
9ae52960 6862
686e302f 6863 [list_ reloadData];
f159ecd4 6864 _trace();
e057ec05 6865}
4941f41d 6866
fa7bb92f
JF
6867- (void) resetView {
6868 if (editing_)
9ae52960 6869 [self editButtonClicked];
e057ec05 6870}
4941f41d 6871
9ae52960 6872- (void) editButtonClicked {
fa7bb92f
JF
6873 if ((editing_ = !editing_))
6874 [list_ reloadData];
f159ecd4 6875 else
fa7bb92f 6876 [delegate_ updateData];
f99f86e2 6877
9ae52960
GP
6878 [[self navigationItem] setTitle:editing_ ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS")];
6879 [[[self navigationItem] rightBarButtonItem] setTitle:[sections_ count] == 0 ? nil : editing_ ? UCLocalize("DONE") : UCLocalize("EDIT")];
6880 [[[self navigationItem] rightBarButtonItem] setStyle:editing_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain];
87c76914
JF
6881}
6882
9e07091a
JF
6883- (UIView *) accessoryView {
6884 return accessory_;
6885}
6886
a75f53e7 6887@end
4941f41d 6888/* }}} */
f441e717 6889/* Changes Controller {{{ */
1e4922b8
JF
6890@interface ChangesController : CYViewController <
6891 UITableViewDataSource,
6892 UITableViewDelegate
6893> {
e057ec05 6894 _transient Database *database_;
2148c5ca 6895 CFMutableArrayRef packages_;
a75f53e7 6896 NSMutableArray *sections_;
5d8f1006 6897 UITableView *list_;
e057ec05 6898 unsigned upgrades_;
77a4e323 6899 BOOL hasSentFirstLoad_;
a75f53e7
JF
6900}
6901
9ae52960 6902- (id) initWithDatabase:(Database *)database delegate:(id)delegate;
e057ec05 6903- (void) reloadData;
a75f53e7 6904
a75f53e7
JF
6905@end
6906
f441e717 6907@implementation ChangesController
686e302f
JF
6908
6909- (void) dealloc {
5d8f1006 6910 [list_ setDelegate:nil];
e057ec05
JF
6911 [list_ setDataSource:nil];
6912
2148c5ca
JF
6913 CFRelease(packages_);
6914
686e302f 6915 [sections_ release];
e057ec05 6916 [list_ release];
686e302f
JF
6917 [super dealloc];
6918}
a75f53e7 6919
9ae52960
GP
6920- (void) viewDidAppear:(BOOL)animated {
6921 [super viewDidAppear:animated];
77a4e323
RP
6922 if (!hasSentFirstLoad_) {
6923 hasSentFirstLoad_ = YES;
6924 [self performSelector:@selector(reloadData) withObject:nil afterDelay:0.0];
6925 } else {
6926 [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
6927 }
9ae52960
GP
6928}
6929
5d8f1006
JF
6930- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
6931 NSInteger count([sections_ count]);
6932 return count == 0 ? 1 : count;
a75f53e7
JF
6933}
6934
5d8f1006
JF
6935- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
6936 if ([sections_ count] == 0)
6937 return nil;
a75f53e7
JF
6938 return [[sections_ objectAtIndex:section] name];
6939}
6940
5d8f1006
JF
6941- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
6942 if ([sections_ count] == 0)
6943 return 0;
6944 return [[sections_ objectAtIndex:section] count];
a75f53e7
JF
6945}
6946
2148c5ca
JF
6947- (Package *) packageAtIndex:(NSUInteger)index {
6948 return (Package *) CFArrayGetValueAtIndex(packages_, index);
6949}
6950
5d8f1006
JF
6951- (Package *) packageAtIndexPath:(NSIndexPath *)path {
6952 Section *section([sections_ objectAtIndex:[path section]]);
6953 NSInteger row([path row]);
2148c5ca 6954 return [self packageAtIndex:([section row] + row)];
a75f53e7
JF
6955}
6956
5d8f1006 6957- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
1e4922b8 6958 PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
5d8f1006
JF
6959 if (cell == nil)
6960 cell = [[[PackageCell alloc] init] autorelease];
6961 [cell setPackage:[self packageAtIndexPath:path]];
6962 return cell;
a75f53e7
JF
6963}
6964
2064d251 6965/*- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path {
5d8f1006 6966 return [PackageCell heightForPackage:[self packageAtIndexPath:path]];
2064d251 6967}*/
a75f53e7 6968
5d8f1006
JF
6969- (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path {
6970 Package *package([self packageAtIndexPath:path]);
f441e717 6971 PackageController *view([delegate_ packageController]);
e057ec05
JF
6972 [view setDelegate:delegate_];
6973 [view setPackage:package];
9ae52960 6974 [[self navigationController] pushViewController:view animated:YES];
5d8f1006 6975 return path;
a75f53e7
JF
6976}
6977
9ae52960 6978- (void) refreshButtonClicked {
1e4922b8 6979 [delegate_ beginUpdate];
24966c5d 6980 [[self navigationItem] setLeftBarButtonItem:nil animated:YES];
b7eb9e84
JF
6981}
6982
9ae52960 6983- (void) upgradeButtonClicked {
e057ec05 6984 [delegate_ distUpgrade];
a75f53e7
JF
6985}
6986
9b62701b 6987- (NSString *) title { return UCLocalize("CHANGES"); }
9ae52960
GP
6988
6989- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
6990 if ((self = [super init]) != nil) {
e057ec05 6991 database_ = database;
9ae52960 6992 [[self navigationItem] setTitle:UCLocalize("CHANGES")];
a75f53e7 6993
2148c5ca
JF
6994 packages_ = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
6995
686e302f 6996 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
a75f53e7 6997
9ae52960 6998 list_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain];
d3bef7bc 6999 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
2064d251 7000 [list_ setRowHeight:73.0f];
9ae52960 7001 [[self view] addSubview:list_];
e057ec05 7002
686e302f 7003 [list_ setDataSource:self];
5d8f1006 7004 [list_ setDelegate:self];
a75f53e7 7005
daf7f6e2 7006 delegate_ = delegate;
a75f53e7
JF
7007 } return self;
7008}
7009
daf7f6e2 7010- (void) _reloadPackages:(NSArray *)packages {
f159ecd4 7011 _trace();
9fdd37d0 7012 for (Package *package in packages)
1c220bde 7013 if ([package upgradableAndEssential:YES] || [package visible])
2148c5ca 7014 CFArrayAppendValue(packages_, package);
4941f41d 7015
f159ecd4 7016 _trace();
2148c5ca 7017 [(NSMutableArray *) packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackageChangesRadix) withContext:NULL];
f159ecd4 7018 _trace();
daf7f6e2
JF
7019}
7020
7021- (void) reloadData {
7022 NSArray *packages = [database_ packages];
7023
2148c5ca
JF
7024 CFArrayRemoveAllValues(packages_);
7025
daf7f6e2
JF
7026 [sections_ removeAllObjects];
7027
dbae8548 7028#if 0
daf7f6e2
JF
7029 UIProgressHUD *hud([delegate_ addProgressHUD]);
7030 // XXX: localize
7031 [hud setText:@"Loading Changes"];
12e0ef8f 7032 //NSLog(@"HUD:%@::%@", delegate_, hud);
daf7f6e2
JF
7033 [self yieldToSelector:@selector(_reloadPackages:) withObject:packages];
7034 [delegate_ removeProgressHUD:hud];
dbae8548
JF
7035#else
7036 [self _reloadPackages:packages];
7037#endif
a75f53e7 7038
61b13cae
JF
7039 Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
7040 Section *ignored = [[[Section alloc] initWithName:UCLocalize("IGNORED_UPGRADES") localize:NO] autorelease];
a75f53e7 7041 Section *section = nil;
f159ecd4 7042 NSDate *last = nil;
686e302f 7043
e057ec05
JF
7044 upgrades_ = 0;
7045 bool unseens = false;
7046
43b742af 7047 CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle));
e057ec05 7048
2148c5ca
JF
7049 for (size_t offset = 0, count = CFArrayGetCount(packages_); offset != count; ++offset) {
7050 Package *package = [self packageAtIndex:offset];
a75f53e7 7051
43b742af 7052 BOOL uae = [package upgradableAndEssential:YES];
6932575e
JF
7053
7054 if (!uae) {
e057ec05 7055 unseens = true;
6932575e 7056 NSDate *seen;
a75f53e7 7057
f441e717 7058 _profile(ChangesController$reloadData$Remember)
6932575e
JF
7059 seen = [package seen];
7060 _end
7061
43b742af 7062 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
f159ecd4 7063 last = seen;
138ae18d 7064
3ff1504e
JF
7065 NSString *name;
7066 if (seen == nil)
61b13cae 7067 name = UCLocalize("UNKNOWN");
3ff1504e 7068 else {
43b742af 7069 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3ff1504e
JF
7070 [name autorelease];
7071 }
7072
f441e717 7073 _profile(ChangesController$reloadData$Allocate)
61b13cae 7074 name = [NSString stringWithFormat:UCLocalize("NEW_AT"), name];
9ee296df 7075 section = [[[Section alloc] initWithName:name row:offset localize:NO] autorelease];
6932575e
JF
7076 [sections_ addObject:section];
7077 _end
686e302f
JF
7078 }
7079
e057ec05 7080 [section addToCount];
f159ecd4
JF
7081 } else if ([package ignored])
7082 [ignored addToCount];
7083 else {
7084 ++upgrades_;
7085 [upgradable addToCount];
686e302f 7086 }
a75f53e7 7087 }
f159ecd4 7088 _trace();
a75f53e7 7089
e057ec05 7090 CFRelease(formatter);
686e302f 7091
e057ec05
JF
7092 if (unseens) {
7093 Section *last = [sections_ lastObject];
7094 size_t count = [last count];
2148c5ca 7095 CFArrayReplaceValues(packages_, CFRangeMake(CFArrayGetCount(packages_) - count, count), NULL, 0);
e057ec05
JF
7096 [sections_ removeLastObject];
7097 }
a75f53e7 7098
f159ecd4
JF
7099 if ([ignored count] != 0)
7100 [sections_ insertObject:ignored atIndex:0];
e057ec05
JF
7101 if (upgrades_ != 0)
7102 [sections_ insertObject:upgradable atIndex:0];
d12c6e70 7103
e057ec05 7104 [list_ reloadData];
2d28b35a 7105
11f75d4f
JF
7106 if (upgrades_ > 0)
7107 [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
7108 initWithTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]]
7109 style:UIBarButtonItemStylePlain
7110 target:self
7111 action:@selector(upgradeButtonClicked)
7112 ] autorelease]];
9ae52960 7113
11f75d4f
JF
7114 if (![delegate_ updating])
7115 [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
7116 initWithTitle:UCLocalize("REFRESH")
7117 style:UIBarButtonItemStylePlain
7118 target:self
7119 action:@selector(refreshButtonClicked)
7120 ] autorelease]];
b6ffa083
JF
7121}
7122
2d28b35a
JF
7123@end
7124/* }}} */
f441e717 7125/* Search Controller {{{ */
1e4922b8
JF
7126@interface SearchController : FilteredPackageController <
7127 UISearchBarDelegate
7128> {
7129 UISearchBar *search_;
e057ec05 7130}
686e302f 7131
9ae52960 7132- (id) initWithDatabase:(Database *)database;
e057ec05 7133- (void) reloadData;
686e302f 7134
2d28b35a
JF
7135@end
7136
f441e717 7137@implementation SearchController
2d28b35a 7138
686e302f 7139- (void) dealloc {
9ae52960 7140 [search_ release];
e057ec05 7141 [super dealloc];
686e302f
JF
7142}
7143
9b62701b 7144- (void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
9ae52960
GP
7145 [packages_ setObject:[search_ text] forFilter:@selector(isUnfilteredAndSearchedForBy:)];
7146 [search_ resignFirstResponder];
5ec44e34 7147 [self reloadData];
e057ec05 7148}
686e302f 7149
9b62701b 7150- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)text {
9ae52960 7151 [packages_ setObject:text forFilter:@selector(isUnfilteredAndSelectedForBy:)];
5ec44e34 7152 [self reloadData];
e057ec05 7153}
686e302f 7154
9b62701b 7155- (NSString *) title { return nil; }
59efd93a 7156
9ae52960 7157- (id) initWithDatabase:(Database *)database {
2c4fc6f1
RP
7158 return [super initWithDatabase:database title:UCLocalize("SEARCH") filter:@selector(isUnfilteredAndSearchedForBy:) with:nil];
7159}
7160
7161- (void)viewDidAppear:(BOOL)animated {
f99f86e2
JF
7162 [super viewDidAppear:animated];
7163 if (!search_) {
1e4922b8 7164 search_ = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] bounds].size.width, 44.0f)];
2c4fc6f1 7165 [search_ layoutSubviews];
9ae52960 7166 [search_ setPlaceholder:UCLocalize("SEARCH_EX")];
2c4fc6f1
RP
7167 UITextField *textField = [search_ searchField];
7168 [textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
9ae52960 7169 [search_ setDelegate:self];
2c4fc6f1
RP
7170 [textField setEnablesReturnKeyAutomatically:NO];
7171 [[self navigationItem] setTitleView:textField];
7172 }
2d28b35a
JF
7173}
7174
f159ecd4
JF
7175- (void) _reloadData {
7176}
686e302f 7177
f159ecd4 7178- (void) reloadData {
f441e717 7179 _profile(SearchController$reloadData)
9ae52960 7180 [packages_ reloadData];
3bddda52
JF
7181 _end
7182 PrintTimes();
9ae52960 7183 [packages_ resetCursor];
f159ecd4 7184}
b6ffa083 7185
583b5809
RP
7186- (void) didSelectPackage:(Package *)package {
7187 [search_ resignFirstResponder];
7188 [super didSelectPackage:package];
7189}
7190
f159ecd4
JF
7191@end
7192/* }}} */
f441e717 7193/* Settings Controller {{{ */
1e4922b8
JF
7194@interface SettingsController : CYViewController <
7195 UITableViewDataSource,
7196 UITableViewDelegate
7197> {
f159ecd4
JF
7198 _transient Database *database_;
7199 NSString *name_;
7200 Package *package_;
31a8e05a 7201 UITableView *table_;
18884e36
JF
7202 UISwitch *subscribedSwitch_;
7203 UISwitch *ignoredSwitch_;
31a8e05a
GP
7204 UITableViewCell *subscribedCell_;
7205 UITableViewCell *ignoredCell_;
f159ecd4 7206}
686e302f 7207
9ae52960 7208- (id) initWithDatabase:(Database *)database package:(NSString *)package;
686e302f 7209
f159ecd4 7210@end
686e302f 7211
f441e717 7212@implementation SettingsController
686e302f 7213
f159ecd4 7214- (void) dealloc {
f159ecd4
JF
7215 [name_ release];
7216 if (package_ != nil)
7217 [package_ release];
7218 [table_ release];
7219 [subscribedSwitch_ release];
7220 [ignoredSwitch_ release];
7221 [subscribedCell_ release];
7222 [ignoredCell_ release];
f99f86e2 7223
f159ecd4
JF
7224 [super dealloc];
7225}
686e302f 7226
31a8e05a 7227- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
f159ecd4
JF
7228 if (package_ == nil)
7229 return 0;
686e302f 7230
31a8e05a 7231 return 1;
2d28b35a
JF
7232}
7233
31a8e05a 7234- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
f159ecd4
JF
7235 if (package_ == nil)
7236 return 0;
a75f53e7 7237
31a8e05a
GP
7238 return 1;
7239}
87c76914 7240
31a8e05a
GP
7241- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
7242 return UCLocalize("SHOW_ALL_CHANGES_EX");
e057ec05 7243}
686e302f 7244
31a8e05a 7245- (void) onSomething:(BOOL)value withKey:(NSString *)key {
f159ecd4
JF
7246 if (package_ == nil)
7247 return;
686e302f 7248
f159ecd4 7249 NSMutableDictionary *metadata([package_ metadata]);
87c76914 7250
f159ecd4
JF
7251 BOOL before;
7252 if (NSNumber *number = [metadata objectForKey:key])
7253 before = [number boolValue];
7254 else
7255 before = NO;
e057ec05 7256
f159ecd4
JF
7257 if (value != before) {
7258 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
7259 Changed_ = true;
f159ecd4
JF
7260 [delegate_ updateData];
7261 }
87c76914
JF
7262}
7263
31a8e05a
GP
7264- (void) onSubscribed:(id)control {
7265 [self onSomething:(int) [control isOn] withKey:@"IsSubscribed"];
e057ec05 7266}
686e302f 7267
31a8e05a
GP
7268- (void) onIgnored:(id)control {
7269 [self onSomething:(int) [control isOn] withKey:@"IsIgnored"];
b6ffa083
JF
7270}
7271
31a8e05a 7272- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
f159ecd4
JF
7273 if (package_ == nil)
7274 return nil;
dbe0f181 7275
31a8e05a
GP
7276 switch ([indexPath row]) {
7277 case 0: return subscribedCell_;
7278 case 1: return ignoredCell_;
f99f86e2 7279
6981ccdf 7280 _nodefault
f159ecd4 7281 }
dbe0f181 7282
f159ecd4 7283 return nil;
dbe0f181
JF
7284}
7285
9b62701b 7286- (NSString *) title { return UCLocalize("SETTINGS"); }
9ae52960
GP
7287
7288- (id) initWithDatabase:(Database *)database package:(NSString *)package {
7289 if ((self = [super init])) {
f159ecd4
JF
7290 database_ = database;
7291 name_ = [package retain];
dbe0f181 7292
9ae52960
GP
7293 [[self navigationItem] setTitle:UCLocalize("SETTINGS")];
7294
31a8e05a
GP
7295 table_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped];
7296 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
9ae52960 7297 [[self view] addSubview:table_];
dbe0f181 7298
18884e36 7299 subscribedSwitch_ = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)];
31a8e05a
GP
7300 [subscribedSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
7301 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:UIControlEventValueChanged];
dbe0f181 7302
18884e36 7303 ignoredSwitch_ = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)];
31a8e05a
GP
7304 [ignoredSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
7305 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventValueChanged];
dbe0f181 7306
31a8e05a
GP
7307 subscribedCell_ = [[UITableViewCell alloc] init];
7308 [subscribedCell_ setText:UCLocalize("SHOW_ALL_CHANGES")];
7309 [subscribedCell_ setAccessoryView:subscribedSwitch_];
ced7fc60 7310 [subscribedCell_ setSelectionStyle:UITableViewCellSelectionStyleNone];
dbe0f181 7311
31a8e05a
GP
7312 ignoredCell_ = [[UITableViewCell alloc] init];
7313 [ignoredCell_ setText:UCLocalize("IGNORE_UPGRADES")];
7314 [ignoredCell_ setAccessoryView:ignoredSwitch_];
ced7fc60 7315 [ignoredCell_ setSelectionStyle:UITableViewCellSelectionStyleNone];
dbe0f181 7316
f159ecd4 7317 [table_ setDataSource:self];
31a8e05a 7318 [table_ setDelegate:self];
f159ecd4
JF
7319 [self reloadData];
7320 } return self;
7321}
dbe0f181 7322
f159ecd4
JF
7323- (void) reloadData {
7324 if (package_ != nil)
7325 [package_ autorelease];
7326 package_ = [database_ packageWithName:name_];
7327 if (package_ != nil) {
7328 [package_ retain];
31a8e05a
GP
7329 [subscribedSwitch_ setOn:([package_ subscribed] ? 1 : 0) animated:NO];
7330 [ignoredSwitch_ setOn:([package_ ignored] ? 1 : 0) animated:NO];
dbe0f181 7331 }
f159ecd4
JF
7332
7333 [table_ reloadData];
dbe0f181
JF
7334}
7335
dbe0f181 7336@end
017b2b71 7337/* }}} */
f441e717
GP
7338/* Signature Controller {{{ */
7339@interface SignatureController : CYBrowserController {
dbe0f181
JF
7340 _transient Database *database_;
7341 NSString *package_;
7342}
7343
9ae52960 7344- (id) initWithDatabase:(Database *)database package:(NSString *)package;
dbe0f181
JF
7345
7346@end
7347
f441e717 7348@implementation SignatureController
dbe0f181
JF
7349
7350- (void) dealloc {
7351 [package_ release];
7352 [super dealloc];
7353}
7354
c2292b80 7355- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
dbe0f181 7356 // XXX: dude!
c2292b80 7357 [super webView:view didClearWindowObject:window forFrame:frame];
dbe0f181
JF
7358}
7359
9ae52960
GP
7360- (id) initWithDatabase:(Database *)database package:(NSString *)package {
7361 if ((self = [super init]) != nil) {
dbe0f181
JF
7362 database_ = database;
7363 package_ = [package retain];
7364 [self reloadData];
7365 } return self;
7366}
7367
7368- (void) reloadData {
7369 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
7370}
7371
c1edf105
GP
7372@end
7373/* }}} */
4cd1145e 7374
c1edf105 7375/* Role Controller {{{ */
1e4922b8
JF
7376@interface RoleController : CYViewController <
7377 UITableViewDataSource,
7378 UITableViewDelegate
7379> {
c1edf105 7380 _transient Database *database_;
a591888a
JF
7381 // XXX: ok, "roledelegate_"?...
7382 _transient id roledelegate_;
c1edf105
GP
7383 UITableView *table_;
7384 UISegmentedControl *segment_;
7385 UIView *container_;
7386}
1e4922b8
JF
7387
7388- (void) showDoneButton;
7389- (void) resizeSegmentedControl;
7390
c1edf105
GP
7391@end
7392
7393@implementation RoleController
7394- (void) dealloc {
7395 [table_ release];
7396 [segment_ release];
7397 [container_ release];
f99f86e2 7398
c1edf105
GP
7399 [super dealloc];
7400}
7401
7402- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
7403 if ((self = [super init])) {
7404 database_ = database;
7405 roledelegate_ = delegate;
f99f86e2 7406
c1edf105 7407 [[self navigationItem] setTitle:UCLocalize("WHO_ARE_YOU")];
f99f86e2 7408
c1edf105 7409 NSArray *items = [NSArray arrayWithObjects:
f99f86e2 7410 UCLocalize("USER"),
c1edf105
GP
7411 UCLocalize("HACKER"),
7412 UCLocalize("DEVELOPER"),
7413 nil];
7414 segment_ = [[UISegmentedControl alloc] initWithItems:items];
7415 container_ = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, 44.0f)];
7416 [container_ addSubview:segment_];
f99f86e2 7417
c1edf105
GP
7418 int index = -1;
7419 if ([Role_ isEqualToString:@"User"]) index = 0;
7420 if ([Role_ isEqualToString:@"Hacker"]) index = 1;
7421 if ([Role_ isEqualToString:@"Developer"]) index = 2;
7422 if (index != -1) {
7423 [segment_ setSelectedSegmentIndex:index];
7424 [self showDoneButton];
7425 }
f99f86e2 7426
c1edf105 7427 [segment_ addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
62d55799 7428 [self resizeSegmentedControl];
f99f86e2 7429
c1edf105
GP
7430 table_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped];
7431 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
7432 [table_ setDelegate:self];
7433 [table_ setDataSource:self];
7434 [[self view] addSubview:table_];
7435 [table_ reloadData];
7436 } return self;
7437}
7438
62d55799
GP
7439- (void) resizeSegmentedControl {
7440 CGFloat width = [[self view] frame].size.width;
7441 [segment_ setFrame:CGRectMake(width / 32.0f, 0, width - (width / 32.0f * 2.0f), 44.0f)];
7442}
7443
ca45de84
GP
7444- (void) viewWillAppear:(BOOL)animated {
7445 [super viewWillAppear:animated];
f99f86e2 7446
62d55799
GP
7447 [self resizeSegmentedControl];
7448}
7449
7450- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
7451 [self resizeSegmentedControl];
7452}
7453
7454- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
7455 [self resizeSegmentedControl];
ca45de84
GP
7456}
7457
c1edf105 7458- (void) save {
90e9a238 7459 NSString *role(nil);
f99f86e2 7460
c1edf105 7461 switch ([segment_ selectedSegmentIndex]) {
fabc4a01
GP
7462 case 0: role = @"User"; break;
7463 case 1: role = @"Hacker"; break;
7464 case 2: role = @"Developer"; break;
c1edf105
GP
7465
7466 _nodefault
7467 }
7468
fabc4a01 7469 if (![role isEqualToString:Role_]) {
90e9a238 7470 bool rolling(Role_ == nil);
fabc4a01 7471 Role_ = role;
f99f86e2 7472
fabc4a01
GP
7473 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7474 Role_, @"Role",
7475 nil];
c1edf105 7476
fabc4a01 7477 [Metadata_ setObject:Settings_ forKey:@"Settings"];
c1edf105 7478
fabc4a01 7479 Changed_ = true;
f99f86e2 7480
90e9a238
JF
7481 if (rolling)
7482 [roledelegate_ loadData];
7483 else
7484 [roledelegate_ updateData];
fabc4a01 7485 }
c1edf105
GP
7486}
7487
7488- (void) segmentChanged:(UISegmentedControl *)control {
7489 [self showDoneButton];
7490}
7491
109d42fa 7492- (void) saveAndClose {
c1edf105 7493 [self save];
109d42fa
GP
7494
7495 [[self navigationItem] setRightBarButtonItem:nil];
c1edf105
GP
7496 [[self navigationController] dismissModalViewControllerAnimated:YES];
7497}
7498
109d42fa
GP
7499- (void) doneButtonClicked {
7500 UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20.0f, 20.0f)] autorelease];
7501 [spinner startAnimating];
7502 UIBarButtonItem *spinItem = [[[UIBarButtonItem alloc] initWithCustomView:spinner] autorelease];
7503 [[self navigationItem] setRightBarButtonItem:spinItem];
7504
7505 [self performSelector:@selector(saveAndClose) withObject:nil afterDelay:0];
7506}
7507
c1edf105 7508- (void) showDoneButton {
11f75d4f 7509 [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
c1edf105
GP
7510 initWithTitle:UCLocalize("DONE")
7511 style:UIBarButtonItemStyleDone
7512 target:self
7513 action:@selector(doneButtonClicked)
11f75d4f 7514 ] autorelease] animated:([[self navigationItem] rightBarButtonItem] == nil)];
c1edf105
GP
7515}
7516
7517- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
62d55799
GP
7518 // XXX: For not having a single cell in the table, this sure is a lot of sections.
7519 return 6;
c1edf105
GP
7520}
7521
7522- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
7523 return 0; // :(
7524}
7525
7526- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
7527 return nil; // This method is required by the protocol.
7528}
7529
7530- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
f99f86e2 7531 if (section == 1)
c1edf105 7532 return UCLocalize("ROLE_EX");
f99f86e2 7533 if (section == 4)
c1edf105
GP
7534 return [NSString stringWithFormat:
7535 @"%@: %@\n%@: %@\n%@: %@",
f99f86e2
JF
7536 UCLocalize("USER"), UCLocalize("USER_EX"),
7537 UCLocalize("HACKER"), UCLocalize("HACKER_EX"),
c1edf105
GP
7538 UCLocalize("DEVELOPER"), UCLocalize("DEVELOPER_EX")
7539 ];
7540 else return nil;
7541}
7542
7543- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
96291f72 7544 return section == 3 ? 44.0f : 0;
c1edf105
GP
7545}
7546
7547- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
96291f72 7548 return section == 3 ? container_ : nil;
c1edf105
GP
7549}
7550
4cd1145e
GP
7551@end
7552/* }}} */
7553/* Stash Controller {{{ */
7554@interface CYStashController : CYViewController {
a591888a
JF
7555 // XXX: just delete these things
7556 _transient UIActivityIndicatorView *spinner_;
7557 _transient UILabel *status_;
7558 _transient UILabel *caption_;
4cd1145e
GP
7559}
7560@end
7561
7562@implementation CYStashController
7563- (id) init {
7564 if ((self = [super init])) {
83aff0db 7565 [[self view] setBackgroundColor:[UIColor viewFlipsideBackgroundColor]];
4cd1145e 7566
11f75d4f 7567 spinner_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
83aff0db 7568 CGRect spinrect = [spinner_ frame];
4cd1145e
GP
7569 spinrect.origin.x = ([[self view] frame].size.width / 2) - (spinrect.size.width / 2);
7570 spinrect.origin.y = [[self view] frame].size.height - 80.0f;
7571 [spinner_ setFrame:spinrect];
7572 [spinner_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin];
83aff0db 7573 [[self view] addSubview:spinner_];
83aff0db 7574 [spinner_ startAnimating];
4cd1145e
GP
7575
7576 CGRect captrect;
7577 captrect.size.width = [[self view] frame].size.width;
7578 captrect.size.height = 40.0f;
7579 captrect.origin.x = 0;
7580 captrect.origin.y = ([[self view] frame].size.height / 2) - (captrect.size.height * 2);
11f75d4f 7581 caption_ = [[[UILabel alloc] initWithFrame:captrect] autorelease];
4cd1145e 7582 [caption_ setText:@"Initializing Filesystem"];
7fc6909e 7583 [caption_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
4cd1145e
GP
7584 [caption_ setFont:[UIFont boldSystemFontOfSize:28.0f]];
7585 [caption_ setTextColor:[UIColor whiteColor]];
7586 [caption_ setBackgroundColor:[UIColor clearColor]];
7587 [caption_ setShadowColor:[UIColor blackColor]];
7588 [caption_ setTextAlignment:UITextAlignmentCenter];
7589 [[self view] addSubview:caption_];
4cd1145e
GP
7590
7591 CGRect statusrect;
7592 statusrect.size.width = [[self view] frame].size.width;
7593 statusrect.size.height = 30.0f;
7594 statusrect.origin.x = 0;
7595 statusrect.origin.y = ([[self view] frame].size.height / 2) - statusrect.size.height;
11f75d4f 7596 status_ = [[[UILabel alloc] initWithFrame:statusrect] autorelease];
7fc6909e 7597 [status_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
4cd1145e
GP
7598 [status_ setText:@"(Cydia will exit when complete.)"];
7599 [status_ setFont:[UIFont systemFontOfSize:16.0f]];
7600 [status_ setTextColor:[UIColor whiteColor]];
7601 [status_ setBackgroundColor:[UIColor clearColor]];
7602 [status_ setShadowColor:[UIColor blackColor]];
7603 [status_ setTextAlignment:UITextAlignmentCenter];
7604 [[self view] addSubview:status_];
4cd1145e
GP
7605 } return self;
7606}
7607
7608- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
7609 return IsWildcat_ || orientation == UIInterfaceOrientationPortrait;
7610}
dbe0f181 7611@end
f159ecd4 7612/* }}} */
dbe0f181 7613
e7a88a8c
GP
7614/* Cydia Container {{{ */
7615@interface CYContainer : UIViewController <ProgressDelegate> {
7616 _transient Database *database_;
7617 RefreshBar *refreshbar_;
7618
7619 bool dropped_;
7620 bool updating_;
a591888a
JF
7621 // XXX: ok, "updatedelegate_"?...
7622 _transient NSObject<CydiaDelegate> *updatedelegate_;
7623 // XXX: can't we query for this variable when we need it?
7624 _transient UITabBarController *root_;
e7a88a8c
GP
7625}
7626
1e4922b8
JF
7627- (void) setTabBarController:(UITabBarController *)controller;
7628
7629- (void) dropBar:(BOOL)animated;
7630- (void) beginUpdate;
7631- (void) raiseBar:(BOOL)animated;
57fde81f 7632- (BOOL) updating;
1e4922b8 7633
e7a88a8c
GP
7634@end
7635
7636@implementation CYContainer
7637
57fde81f
GP
7638- (BOOL) _reallyWantsFullScreenLayout {
7639 return YES;
7640}
7641
15b41c77
GP
7642// NOTE: UIWindow only sends the top controller these messages,
7643// So we have to forward them on.
7644
7645- (void) viewDidAppear:(BOOL)animated {
7646 [super viewDidAppear:animated];
7647 [root_ viewDidAppear:animated];
7648}
7649
7650- (void) viewWillAppear:(BOOL)animated {
7651 [super viewWillAppear:animated];
7652 [root_ viewWillAppear:animated];
7653}
7654
7655- (void) viewDidDisappear:(BOOL)animated {
7656 [super viewDidDisappear:animated];
7657 [root_ viewDidDisappear:animated];
7658}
7659
7660- (void) viewWillDisappear:(BOOL)animated {
7661 [super viewWillDisappear:animated];
7662 [root_ viewWillDisappear:animated];
7663}
7664
e7a88a8c 7665- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
832c0799 7666 return ![updatedelegate_ hudIsShowing] && (IsWildcat_ || orientation == UIInterfaceOrientationPortrait);
e7a88a8c
GP
7667}
7668
1e4922b8 7669- (void) setTabBarController:(UITabBarController *)controller {
e7a88a8c
GP
7670 root_ = controller;
7671 [[self view] addSubview:[root_ view]];
7672}
7673
7674- (void) setUpdate:(NSDate *)date {
7675 [self beginUpdate];
7676}
7677
7678- (void) beginUpdate {
7679 [self dropBar:YES];
7680 [refreshbar_ start];
f99f86e2 7681
e7a88a8c 7682 updating_ = true;
f99f86e2 7683
e7a88a8c
GP
7684 [NSThread
7685 detachNewThreadSelector:@selector(performUpdate)
7686 toTarget:self
7687 withObject:nil
7688 ];
7689}
7690
7691- (void) performUpdate { _pooled
7692 Status status;
7693 status.setDelegate(self);
7694 [database_ updateWithStatus:status];
7695
7696 [self
7697 performSelectorOnMainThread:@selector(completeUpdate)
7698 withObject:nil
7699 waitUntilDone:NO
7700 ];
7701}
7702
7703- (void) completeUpdate {
96291f72
JF
7704 if (!updating_)
7705 return;
e7a88a8c 7706 updating_ = false;
f99f86e2 7707
e7a88a8c
GP
7708 [self raiseBar:YES];
7709 [refreshbar_ stop];
7710 [updatedelegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
7711}
7712
7713- (void) cancelUpdate {
24966c5d
GP
7714 updating_ = false;
7715 [self raiseBar:YES];
7716 [refreshbar_ stop];
7717 [updatedelegate_ performSelector:@selector(updateData) withObject:nil afterDelay:0];
e7a88a8c
GP
7718}
7719
7720- (void) cancelPressed {
7721 [self cancelUpdate];
7722}
7723
7724- (BOOL) updating {
7725 return updating_;
7726}
7727
7728- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
7729 [refreshbar_ setPrompt:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]];
7730}
7731
7732- (void) startProgress {
7733}
7734
7735- (void) setProgressTitle:(NSString *)title {
7736 [self
7737 performSelectorOnMainThread:@selector(_setProgressTitle:)
7738 withObject:title
7739 waitUntilDone:YES
7740 ];
7741}
7742
7743- (bool) isCancelling:(size_t)received {
7744 return !updating_;
7745}
7746
7747- (void) setProgressPercent:(float)percent {
7748 [self
7749 performSelectorOnMainThread:@selector(_setProgressPercent:)
7750 withObject:[NSNumber numberWithFloat:percent]
7751 waitUntilDone:YES
7752 ];
7753}
7754
7755- (void) addProgressOutput:(NSString *)output {
7756 [self
7757 performSelectorOnMainThread:@selector(_addProgressOutput:)
7758 withObject:output
7759 waitUntilDone:YES
7760 ];
7761}
7762
7763- (void) _setProgressTitle:(NSString *)title {
7764 [refreshbar_ setPrompt:title];
7765}
7766
7767- (void) _setProgressPercent:(NSNumber *)percent {
7768 [refreshbar_ setProgress:[percent floatValue]];
7769}
7770
7771- (void) _addProgressOutput:(NSString *)output {
7772}
7773
7774- (void) setUpdateDelegate:(id)delegate {
7775 updatedelegate_ = delegate;
7776}
7777
149ed89e
GP
7778- (CGFloat) statusBarHeight {
7779 if (UIInterfaceOrientationIsPortrait([self interfaceOrientation])) {
7780 return [[UIApplication sharedApplication] statusBarFrame].size.height;
7781 } else {
7782 return [[UIApplication sharedApplication] statusBarFrame].size.width;
7783 }
7784}
7785
e7a88a8c 7786- (void) dropBar:(BOOL)animated {
96291f72
JF
7787 if (dropped_)
7788 return;
e7a88a8c
GP
7789 dropped_ = true;
7790
7791 [[self view] addSubview:refreshbar_];
7792
149ed89e 7793 CGFloat sboffset = [self statusBarHeight];
57fde81f 7794
283fc596 7795 CGRect barframe = [refreshbar_ frame];
57fde81f
GP
7796 barframe.origin.y = sboffset;
7797 [refreshbar_ setFrame:barframe];
7798
96291f72
JF
7799 if (animated)
7800 [UIView beginAnimations:nil context:NULL];
e7a88a8c 7801 CGRect viewframe = [[root_ view] frame];
57fde81f
GP
7802 viewframe.origin.y += barframe.size.height + sboffset;
7803 viewframe.size.height -= barframe.size.height + sboffset;
e7a88a8c 7804 [[root_ view] setFrame:viewframe];
96291f72
JF
7805 if (animated)
7806 [UIView commitAnimations];
e7a88a8c 7807
15b41c77
GP
7808 // Ensure bar has the proper width for our view, it might have changed
7809 barframe.size.width = viewframe.size.width;
7810 [refreshbar_ setFrame:barframe];
7811
e7a88a8c
GP
7812 // XXX: fix Apple's layout bug
7813 [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
7814}
7815
7816- (void) raiseBar:(BOOL)animated {
96291f72
JF
7817 if (!dropped_)
7818 return;
e7a88a8c
GP
7819 dropped_ = false;
7820
7821 [refreshbar_ removeFromSuperview];
7822
283fc596 7823 CGFloat sboffset = [self statusBarHeight];
57fde81f 7824
96291f72
JF
7825 if (animated)
7826 [UIView beginAnimations:nil context:NULL];
e7a88a8c
GP
7827 CGRect barframe = [refreshbar_ frame];
7828 CGRect viewframe = [[root_ view] frame];
57fde81f
GP
7829 viewframe.origin.y -= barframe.size.height + sboffset;
7830 viewframe.size.height += barframe.size.height + sboffset;
e7a88a8c 7831 [[root_ view] setFrame:viewframe];
96291f72
JF
7832 if (animated)
7833 [UIView commitAnimations];
e7a88a8c
GP
7834
7835 // XXX: fix Apple's layout bug
7836 [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
7837}
7838
62d55799 7839- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
d6011e10
RP
7840 // XXX: fix Apple's layout bug
7841 [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
7842}
7843
15b41c77 7844- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
e7a88a8c
GP
7845 if (dropped_) {
7846 [self raiseBar:NO];
7847 [self dropBar:NO];
7848 }
f99f86e2 7849
e7a88a8c
GP
7850 // XXX: fix Apple's layout bug
7851 [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
7852}
7853
57fde81f
GP
7854- (void) statusBarFrameChanged:(NSNotification *)notification {
7855 if (dropped_) {
7856 [self raiseBar:NO];
7857 [self dropBar:NO];
7858 }
7859}
7860
e7a88a8c
GP
7861- (void) dealloc {
7862 [refreshbar_ release];
57fde81f 7863 [[NSNotificationCenter defaultCenter] removeObserver:self];
e7a88a8c
GP
7864 [super dealloc];
7865}
7866
d5653f96 7867- (id) initWithDatabase:(Database *)database {
e7a88a8c
GP
7868 if ((self = [super init]) != nil) {
7869 database_ = database;
7870
15b41c77 7871 [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
57fde81f 7872 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
15b41c77
GP
7873
7874 refreshbar_ = [[RefreshBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [UINavigationBar defaultSize].height) delegate:self];
e7a88a8c
GP
7875 } return self;
7876}
7877
7878@end
7879/* }}} */
7880
9ae52960
GP
7881typedef enum {
7882 kCydiaTag = 0,
7883 kSectionsTag = 1,
7884 kChangesTag = 2,
7885 kManageTag = 3,
7886 kInstalledTag = 4,
7887 kSourcesTag = 5,
7888 kSearchTag = 6
7889} CYTabTag;
d3bef7bc 7890
4941f41d 7891@interface Cydia : UIApplication <
f441e717
GP
7892 ConfirmationControllerDelegate,
7893 ProgressControllerDelegate,
9b62701b 7894 CydiaDelegate,
18884e36
JF
7895 UINavigationControllerDelegate,
7896 UITabBarControllerDelegate
4941f41d 7897> {
a591888a
JF
7898 // XXX: evaluate all fields for _transient
7899
a75f53e7 7900 UIWindow *window_;
e7a88a8c 7901 CYContainer *container_;
18884e36 7902 CYTabBarController *tabbar_;
3319715b 7903
9e07091a 7904 NSMutableArray *essential_;
3319715b 7905 NSMutableArray *broken_;
a75f53e7
JF
7906
7907 Database *database_;
a75f53e7 7908
84848968 7909 int tag_;
686e302f
JF
7910
7911 UIKeyboard *keyboard_;
832c0799 7912 int huds_;
e25e221f 7913
f441e717
GP
7914 SectionsController *sections_;
7915 ChangesController *changes_;
7916 ManageController *manage_;
7917 SearchController *search_;
9ae52960 7918 SourceTable *sources_;
f441e717 7919 InstalledController *installed_;
9ae52960 7920 id queueDelegate_;
6932575e 7921
4cd1145e
GP
7922 CYStashController *stash_;
7923
e7a88a8c 7924 bool loaded_;
a75f53e7
JF
7925}
7926
881fe77f
JF
7927- (CYViewController *) _pageForURL:(NSURL *)url withClass:(Class)_class;
7928- (void) setPage:(CYViewController *)page;
90e9a238 7929- (void) loadData;
6981ccdf 7930
a75f53e7
JF
7931@end
7932
6981ccdf 7933static _finline void _setHomePage(Cydia *self) {
f441e717 7934 [self setPage:[self _pageForURL:[NSURL URLWithString:CydiaURL(@"")] withClass:[HomeController class]]];
6981ccdf
JF
7935}
7936
a75f53e7
JF
7937@implementation Cydia
7938
9ae52960 7939- (void) beginUpdate {
e7a88a8c 7940 [container_ beginUpdate];
9ae52960
GP
7941}
7942
7943- (BOOL) updating {
e7a88a8c 7944 return [container_ updating];
9ae52960
GP
7945}
7946
5ec44e34
JF
7947- (UIView *) rotatingContentViewForWindow:(UIWindow *)window {
7948 return window_;
7949}
7950
3319715b
JF
7951- (void) _loaded {
7952 if ([broken_ count] != 0) {
7953 int count = [broken_ count];
7954
7d3e75f4 7955 UIAlertView *alert = [[[UIAlertView alloc]
61b13cae 7956 initWithTitle:(count == 1 ? UCLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:UCLocalize("HALFINSTALLED_PACKAGES"), count])
9ae52960
GP
7957 message:UCLocalize("HALFINSTALLED_PACKAGE_EX")
7958 delegate:self
7959 cancelButtonTitle:UCLocalize("FORCIBLY_CLEAR")
7960 otherButtonTitles:UCLocalize("TEMPORARY_IGNORE"), nil
3319715b
JF
7961 ] autorelease];
7962
7d3e75f4
GP
7963 [alert setContext:@"fixhalf"];
7964 [alert show];
3319715b
JF
7965 } else if (!Ignored_ && [essential_ count] != 0) {
7966 int count = [essential_ count];
7967
7d3e75f4 7968 UIAlertView *alert = [[[UIAlertView alloc]
61b13cae 7969 initWithTitle:(count == 1 ? UCLocalize("ESSENTIAL_UPGRADE") : [NSString stringWithFormat:UCLocalize("ESSENTIAL_UPGRADES"), count])
9ae52960
GP
7970 message:UCLocalize("ESSENTIAL_UPGRADE_EX")
7971 delegate:self
7972 cancelButtonTitle:UCLocalize("TEMPORARY_IGNORE")
7d3e75f4 7973 otherButtonTitles:UCLocalize("UPGRADE_ESSENTIAL"), UCLocalize("COMPLETE_UPGRADE"), nil
3319715b
JF
7974 ] autorelease];
7975
7d3e75f4
GP
7976 [alert setContext:@"upgrade"];
7977 [alert show];
3319715b
JF
7978 }
7979}
7980
419a9efd
JF
7981- (void) _saveConfig {
7982 if (Changed_) {
7983 _trace();
7984 NSString *error(nil);
7985 if (NSData *data = [NSPropertyListSerialization dataFromPropertyList:Metadata_ format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error]) {
7986 _trace();
7987 NSError *error(nil);
7988 if (![data writeToFile:@"/var/lib/cydia/metadata.plist" options:NSAtomicWrite error:&error])
7989 NSLog(@"failure to save metadata data: %@", error);
7990 _trace();
7991 } else {
7992 NSLog(@"failure to serialize metadata: %@", error);
7993 return;
7994 }
7995
7996 Changed_ = false;
7997 }
7998}
7999
8000- (void) _updateData {
8001 [self _saveConfig];
8002
8003 /* XXX: this is just stupid */
84848968 8004 if (tag_ != 1 && sections_ != nil)
419a9efd 8005 [sections_ reloadData];
84848968 8006 if (tag_ != 2 && changes_ != nil)
419a9efd 8007 [changes_ reloadData];
84848968 8008 if (tag_ != 4 && search_ != nil)
419a9efd
JF
8009 [search_ reloadData];
8010
1e4922b8 8011 [(CYNavigationController *)[tabbar_ selectedViewController] reloadData];
9ae52960
GP
8012}
8013
8014- (int)indexOfTabWithTag:(int)tag {
8015 int i = 0;
8016 for (UINavigationController *controller in [tabbar_ viewControllers]) {
96291f72
JF
8017 if ([[controller tabBarItem] tag] == tag)
8018 return i;
9ae52960
GP
8019 i += 1;
8020 }
f99f86e2 8021
9ae52960 8022 return -1;
419a9efd
JF
8023}
8024
d56a0c9b 8025- (void) _refreshIfPossible {
e7a88a8c 8026 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
f99f86e2 8027
24966c5d
GP
8028 bool recently = false;
8029 NSDate *update([Metadata_ objectForKey:@"LastUpdate"]);
8030 if (update != nil) {
8031 NSTimeInterval interval([update timeIntervalSinceNow]);
8032 if (interval <= 0 && interval > -(15*60))
8033 recently = true;
8034 }
8035
8036 // Don't automatic refresh if:
8037 // - We already refreshed recently.
8038 // - We already auto-refreshed this launch.
8039 // - Auto-refresh is disabled.
8040 if (recently || loaded_ || ManualRefresh) {
8041 [self performSelectorOnMainThread:@selector(_loaded) withObject:nil waitUntilDone:NO];
8042
8043 // If we are cancelling due to ManualRefresh or a recent refresh
8044 // we need to make sure it knows it's already loaded.
8045 loaded_ = true;
8046 return;
8047 } else {
8048 // We are going to load, so remember that.
8049 loaded_ = true;
8050 }
8051
d7a235a6
JF
8052 SCNetworkReachabilityFlags flags; {
8053 SCNetworkReachabilityRef reachability(SCNetworkReachabilityCreateWithName(NULL, "cydia.saurik.com"));
8054 SCNetworkReachabilityGetFlags(reachability, &flags);
8055 CFRelease(reachability);
8056 }
8057
8058 // XXX: this elaborate mess is what Apple is using to determine this? :(
8059 // XXX: do we care if the user has to intervene? maybe that's ok?
8060 bool reachable(
8061 (flags & kSCNetworkReachabilityFlagsReachable) != 0 && (
8062 (flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0 || (
8063 (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0 ||
8064 (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0
8065 ) && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0 ||
8066 (flags & kSCNetworkReachabilityFlagsIsWWAN) != 0
8067 )
8068 );
f99f86e2 8069
24966c5d
GP
8070 // If we can reach the server, auto-refresh!
8071 if (reachable)
e7a88a8c 8072 [container_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO];
d56a0c9b 8073
e7a88a8c 8074 [pool release];
d56a0c9b
GP
8075}
8076
8077- (void) refreshIfPossible {
e7a88a8c 8078 [NSThread detachNewThreadSelector:@selector(_refreshIfPossible) toTarget:self withObject:nil];
d56a0c9b
GP
8079}
8080
e057ec05 8081- (void) _reloadData {
7fc6909e
GP
8082 UIProgressHUD *hud(loaded_ ? [self addProgressHUD] : nil);
8083 [hud setText:UCLocalize("RELOADING_DATA")];
a75f53e7 8084
7398a389 8085 [database_ yieldToSelector:@selector(reloadData) withObject:nil];
7398a389 8086
96291f72
JF
8087 if (hud != nil)
8088 [self removeProgressHUD:hud];
d12c6e70 8089
e057ec05 8090 size_t changes(0);
3319715b 8091
9e07091a 8092 [essential_ removeAllObjects];
3319715b 8093 [broken_ removeAllObjects];
686e302f 8094
6981ccdf 8095 NSArray *packages([database_ packages]);
f464053e 8096 for (Package *package in packages) {
3319715b
JF
8097 if ([package half])
8098 [broken_ addObject:package];
238b07ce 8099 if ([package upgradableAndEssential:NO]) {
9e07091a
JF
8100 if ([package essential])
8101 [essential_ addObject:package];
e057ec05 8102 ++changes;
9e07091a 8103 }
e057ec05 8104 }
686e302f 8105
24966c5d 8106 UITabBarItem *changesItem = [[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem];
e057ec05
JF
8107 if (changes != 0) {
8108 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
24966c5d 8109 [changesItem setBadgeValue:badge];
3203e099 8110 [changesItem setAnimatedBadge:([essential_ count] > 0)];
f99f86e2 8111
6932575e
JF
8112 if ([self respondsToSelector:@selector(setApplicationBadge:)])
8113 [self setApplicationBadge:badge];
8114 else
8115 [self setApplicationBadgeString:badge];
d12c6e70 8116 } else {
24966c5d
GP
8117 [changesItem setBadgeValue:nil];
8118 [changesItem setAnimatedBadge:NO];
f99f86e2 8119
6932575e
JF
8120 if ([self respondsToSelector:@selector(removeApplicationBadge)])
8121 [self removeApplicationBadge];
8122 else // XXX: maybe use setApplicationBadgeString also?
8123 [self setApplicationIconBadgeNumber:0];
d12c6e70 8124 }
686e302f 8125
419a9efd 8126 [self _updateData];
fa7bb92f 8127
e7a88a8c 8128 [self refreshIfPossible];
fa7bb92f
JF
8129}
8130
faf4eb4f 8131- (void) updateData {
419a9efd 8132 [self _updateData];
686e302f
JF
8133}
8134
faf4eb4f
JF
8135- (void) update_ {
8136 [database_ update];
8137}
8138
8139- (void) syncData {
6981ccdf 8140 FILE *file(fopen("/etc/apt/sources.list.d/cydia.list", "w"));
faf4eb4f
JF
8141 _assert(file != NULL);
8142
6981ccdf
JF
8143 for (NSString *key in [Sources_ allKeys]) {
8144 NSDictionary *source([Sources_ objectForKey:key]);
faf4eb4f
JF
8145
8146 fprintf(file, "%s %s %s\n",
8147 [[source objectForKey:@"Type"] UTF8String],
8148 [[source objectForKey:@"URI"] UTF8String],
8149 [[source objectForKey:@"Distribution"] UTF8String]
8150 );
8151 }
8152
8153 fclose(file);
8154
8155 [self _saveConfig];
8156
f441e717 8157 ProgressController *progress = [[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease];
9b62701b 8158 CYNavigationController *navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease];
96291f72
JF
8159 if (IsWildcat_)
8160 [navigation setModalPresentationStyle:UIModalPresentationFormSheet];
e7a88a8c 8161 [container_ presentModalViewController:navigation animated:YES];
9ae52960
GP
8162
8163 [progress
faf4eb4f
JF
8164 detachNewThreadSelector:@selector(update_)
8165 toTarget:self
8166 withObject:nil
61b13cae 8167 title:UCLocalize("UPDATING_SOURCES")
faf4eb4f
JF
8168 ];
8169}
8170
e057ec05
JF
8171- (void) reloadData {
8172 @synchronized (self) {
9ae52960 8173 [self _reloadData];
e057ec05 8174 }
686e302f
JF
8175}
8176
8177- (void) resolve {
8178 pkgProblemResolver *resolver = [database_ resolver];
8179
8180 resolver->InstallProtect();
8181 if (!resolver->Resolve(true))
8182 _error->Discard();
4941f41d
JF
8183}
8184
04700693 8185- (CGRect) popUpBounds {
9ae52960 8186 return [[tabbar_ view] bounds];
04700693
JF
8187}
8188
6981ccdf
JF
8189- (bool) perform {
8190 if (![database_ prepare])
8191 return false;
242bcc6d 8192
f441e717 8193 ConfirmationController *page([[[ConfirmationController alloc] initWithDatabase:database_] autorelease]);
f464053e 8194 [page setDelegate:self];
a591888a 8195 CYNavigationController *confirm_([[[CYNavigationController alloc] initWithRootViewController:page] autorelease]);
9ae52960 8196 [confirm_ setDelegate:self];
242bcc6d 8197
96291f72
JF
8198 if (IsWildcat_)
8199 [confirm_ setModalPresentationStyle:UIModalPresentationFormSheet];
e7a88a8c 8200 [container_ presentModalViewController:confirm_ animated:YES];
6981ccdf
JF
8201
8202 return true;
2d28b35a
JF
8203}
8204
3ff1504e
JF
8205- (void) queue {
8206 @synchronized (self) {
8207 [self perform];
8208 }
8209}
8210
8211- (void) clearPackage:(Package *)package {
8212 @synchronized (self) {
8213 [package clear];
8214 [self resolve];
8215 [self perform];
8216 }
8217}
8218
daf7f6e2
JF
8219- (void) installPackages:(NSArray *)packages {
8220 @synchronized (self) {
8221 for (Package *package in packages)
8222 [package install];
8223 [self resolve];
8224 [self perform];
8225 }
8226}
8227
e057ec05
JF
8228- (void) installPackage:(Package *)package {
8229 @synchronized (self) {
8230 [package install];
8231 [self resolve];
8232 [self perform];
8233 }
8234}
8235
8236- (void) removePackage:(Package *)package {
8237 @synchronized (self) {
8238 [package remove];
8239 [self resolve];
8240 [self perform];
8241 }
8242}
8243
8244- (void) distUpgrade {
8245 @synchronized (self) {
6981ccdf
JF
8246 if (![database_ upgrade])
8247 return;
e057ec05
JF
8248 [self perform];
8249 }
686e302f
JF
8250}
8251
3ff1504e 8252- (void) complete {
e057ec05 8253 @synchronized (self) {
e057ec05
JF
8254 [self _reloadData];
8255 }
2d28b35a
JF
8256}
8257
9ae52960 8258- (void) confirmWithNavigationController:(UINavigationController *)navigation {
f441e717 8259 ProgressController *progress = [[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease];
f99f86e2 8260
9ae52960
GP
8261 if (navigation != nil) {
8262 [navigation pushViewController:progress animated:YES];
8263 } else {
15b41c77 8264 navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease];
96291f72
JF
8265 if (IsWildcat_)
8266 [navigation setModalPresentationStyle:UIModalPresentationFormSheet];
e7a88a8c 8267 [container_ presentModalViewController:navigation animated:YES];
9ae52960 8268 }
f99f86e2 8269
9ae52960 8270 [progress
4941f41d
JF
8271 detachNewThreadSelector:@selector(perform)
8272 toTarget:database_
8273 withObject:nil
61b13cae 8274 title:UCLocalize("RUNNING")
4941f41d
JF
8275 ];
8276}
8277
ba6cbb36 8278- (void) progressControllerIsComplete:(ProgressController *)progress {
3ff1504e 8279 [self complete];
a75f53e7
JF
8280}
8281
881fe77f 8282- (void) setPage:(CYViewController *)page {
e057ec05 8283 [page setDelegate:self];
9ae52960 8284
1e4922b8 8285 CYNavigationController *navController = (CYNavigationController *) [tabbar_ selectedViewController];
eceab8e6 8286 [navController setViewControllers:[NSArray arrayWithObject:page]];
96291f72
JF
8287 for (CYNavigationController *page in [tabbar_ viewControllers])
8288 if (page != navController)
8289 [page setViewControllers:nil];
a75f53e7
JF
8290}
8291
881fe77f 8292- (CYViewController *) _pageForURL:(NSURL *)url withClass:(Class)_class {
9ae52960 8293 CYBrowserController *browser = [[[_class alloc] init] autorelease];
faf4eb4f 8294 [browser loadURL:url];
e057ec05 8295 return browser;
4941f41d
JF
8296}
8297
f441e717 8298- (SectionsController *) sectionsController {
6932575e 8299 if (sections_ == nil)
f441e717 8300 sections_ = [[SectionsController alloc] initWithDatabase:database_];
6932575e
JF
8301 return sections_;
8302}
8303
f441e717 8304- (ChangesController *) changesController {
5ec44e34 8305 if (changes_ == nil)
f441e717 8306 changes_ = [[ChangesController alloc] initWithDatabase:database_ delegate:self];
5ec44e34
JF
8307 return changes_;
8308}
8309
f441e717 8310- (ManageController *) manageController {
9ae52960 8311 if (manage_ == nil) {
f441e717 8312 manage_ = (ManageController *) [[self
5ec44e34 8313 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
f441e717 8314 withClass:[ManageController class]
5ec44e34 8315 ] retain];
96291f72
JF
8316 if (!IsWildcat_)
8317 queueDelegate_ = manage_;
9ae52960 8318 }
5ec44e34
JF
8319 return manage_;
8320}
8321
f441e717 8322- (SearchController *) searchController {
5ec44e34 8323 if (search_ == nil)
f441e717 8324 search_ = [[SearchController alloc] initWithDatabase:database_];
5ec44e34
JF
8325 return search_;
8326}
8327
f441e717 8328- (SourceTable *) sourcesController {
9ae52960
GP
8329 if (sources_ == nil)
8330 sources_ = [[SourceTable alloc] initWithDatabase:database_];
8331 return sources_;
8332}
8333
f441e717 8334- (InstalledController *) installedController {
9ae52960 8335 if (installed_ == nil) {
f441e717 8336 installed_ = [[InstalledController alloc] initWithDatabase:database_];
96291f72
JF
8337 if (IsWildcat_)
8338 queueDelegate_ = installed_;
9ae52960
GP
8339 }
8340 return installed_;
8341}
8342
18884e36 8343- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
9ae52960 8344 int tag = [[viewController tabBarItem] tag];
97959670 8345 if (tag == tag_) {
1e4922b8 8346 [(CYNavigationController *)[tabbar_ selectedViewController] popToRootViewControllerAnimated:YES];
97959670 8347 return;
9ae52960 8348 } else if (tag_ == 1) {
f441e717 8349 [[self sectionsController] resetView];
9ae52960 8350 }
a75f53e7 8351
7e986211 8352 switch (tag) {
9ae52960 8353 case kCydiaTag: _setHomePage(self); break;
e057ec05 8354
f441e717
GP
8355 case kSectionsTag: [self setPage:[self sectionsController]]; break;
8356 case kChangesTag: [self setPage:[self changesController]]; break;
8357 case kManageTag: [self setPage:[self manageController]]; break;
8358 case kInstalledTag: [self setPage:[self installedController]]; break;
8359 case kSourcesTag: [self setPage:[self sourcesController]]; break;
8360 case kSearchTag: [self setPage:[self searchController]]; break;
e057ec05 8361
6981ccdf 8362 _nodefault
a75f53e7
JF
8363 }
8364
7e986211 8365 tag_ = tag;
7e986211
JF
8366}
8367
c1edf105 8368- (void) showSettings {
a591888a
JF
8369 RoleController *role = [[[RoleController alloc] initWithDatabase:database_ delegate:self] autorelease];
8370 CYNavigationController *nav = [[[CYNavigationController alloc] initWithRootViewController:role] autorelease];
96291f72
JF
8371 if (IsWildcat_)
8372 [nav setModalPresentationStyle:UIModalPresentationFormSheet];
c1edf105 8373 [container_ presentModalViewController:nav animated:YES];
faf4eb4f
JF
8374}
8375
f441e717 8376- (void) setPackageController:(PackageController *)view {
b452841e
JF
8377 WebThreadLock();
8378 [view setPackage:nil];
b452841e
JF
8379 WebThreadUnlock();
8380}
8381
f441e717
GP
8382- (PackageController *) _packageController {
8383 return [[[PackageController alloc] initWithDatabase:database_] autorelease];
6932575e
JF
8384}
8385
f441e717 8386- (PackageController *) packageController {
f441e717 8387 return [self _packageController];
6932575e
JF
8388}
8389
19044e4f
GP
8390// Returns the navigation controller for the queuing badge.
8391- (id) queueBadgeController {
8392 int index = [self indexOfTabWithTag:kManageTag];
96291f72
JF
8393 if (index == -1)
8394 index = [self indexOfTabWithTag:kInstalledTag];
283fc596 8395
19044e4f
GP
8396 return [[tabbar_ viewControllers] objectAtIndex:index];
8397}
8398
1ca35d78
GP
8399- (void) cancelAndClear:(bool)clear {
8400 @synchronized (self) {
8401 if (clear) {
30746bd9 8402 [database_ clear];
9ae52960 8403
283fc596 8404 // Stop queuing.
9ae52960 8405 Queuing_ = false;
19044e4f 8406 [[[self queueBadgeController] tabBarItem] setBadgeValue:nil];
1ca35d78 8407 } else {
283fc596 8408 // Start queuing.
9ae52960 8409 Queuing_ = true;
19044e4f 8410 [[[self queueBadgeController] tabBarItem] setBadgeValue:UCLocalize("Q_D")];
283fc596
JF
8411 }
8412
41c6e6d1 8413 [self _updateData];
19044e4f 8414 [queueDelegate_ queueStatusDidChange];
1ca35d78 8415 }
7d3e75f4 8416}
faf4eb4f 8417
9ae52960
GP
8418- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
8419 NSString *context([alert context]);
f99f86e2 8420
9ae52960
GP
8421 if ([context isEqualToString:@"fixhalf"]) {
8422 if (button == [alert firstOtherButtonIndex]) {
7d3e75f4
GP
8423 @synchronized (self) {
8424 for (Package *broken in broken_) {
8425 [broken remove];
8426
8427 NSString *id = [broken id];
8428 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
8429 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
8430 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
8431 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
8432 }
faf4eb4f 8433
7d3e75f4
GP
8434 [self resolve];
8435 [self perform];
8436 }
9ae52960 8437 } else if (button == [alert cancelButtonIndex]) {
7d3e75f4
GP
8438 [broken_ removeAllObjects];
8439 [self _loaded];
faf4eb4f
JF
8440 }
8441
7d3e75f4 8442 [alert dismissWithClickedButtonIndex:-1 animated:YES];
a5dd312c 8443 } else if ([context isEqualToString:@"upgrade"]) {
7d3e75f4
GP
8444 if (button == [alert firstOtherButtonIndex]) {
8445 @synchronized (self) {
8446 for (Package *essential in essential_)
8447 [essential install];
faf4eb4f 8448
7d3e75f4
GP
8449 [self resolve];
8450 [self perform];
8451 }
8452 } else if (button == [alert firstOtherButtonIndex] + 1) {
8453 [self distUpgrade];
8454 } else if (button == [alert cancelButtonIndex]) {
8455 Ignored_ = YES;
faf4eb4f
JF
8456 }
8457
7d3e75f4 8458 [alert dismissWithClickedButtonIndex:-1 animated:YES];
a5dd312c 8459 }
faf4eb4f
JF
8460}
8461
6981ccdf
JF
8462- (void) system:(NSString *)command { _pooled
8463 system([command UTF8String]);
8464}
8465
8466- (void) applicationWillSuspend {
8467 [database_ clean];
8468 [super applicationWillSuspend];
b26eb97d
JF
8469}
8470
832c0799
GP
8471- (BOOL) hudIsShowing {
8472 return (huds_ > 0);
8473}
8474
b26eb97d 8475- (void) applicationSuspend:(__GSEvent *)event {
ea0e9eb1
GP
8476 // Use external process status API internally.
8477 // This is probably a really bad idea.
8478 uint64_t status = 0;
8479 int notify_token;
8480 if (notify_register_check("com.saurik.Cydia.status", &notify_token) == NOTIFY_STATUS_OK) {
8481 notify_get_state(notify_token, &status);
8482 notify_cancel(notify_token);
8483 }
8484
832c0799 8485 if (![self hudIsShowing] && status == 0)
b26eb97d 8486 [super applicationSuspend:event];
b26eb97d
JF
8487}
8488
fa7bb92f 8489- (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
832c0799 8490 if (![self hudIsShowing])
fa7bb92f
JF
8491 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
8492}
8493
8494- (void) _setSuspended:(BOOL)value {
832c0799 8495 if (![self hudIsShowing])
fa7bb92f
JF
8496 [super _setSuspended:value];
8497}
8498
faf4eb4f 8499- (UIProgressHUD *) addProgressHUD {
7398a389 8500 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
d3bef7bc
JF
8501 [hud setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
8502
7398a389 8503 [window_ setUserInteractionEnabled:NO];
faf4eb4f 8504 [hud show:YES];
12e0ef8f
GP
8505
8506 UIViewController *target = container_;
8507 while ([target modalViewController] != nil) target = [target modalViewController];
8508 [[target view] addSubview:hud];
8509
832c0799 8510 huds_++;
faf4eb4f
JF
8511 return hud;
8512}
8513
7398a389
JF
8514- (void) removeProgressHUD:(UIProgressHUD *)hud {
8515 [hud show:NO];
8516 [hud removeFromSuperview];
8517 [window_ setUserInteractionEnabled:YES];
832c0799 8518 huds_--;
7398a389
JF
8519}
8520
881fe77f 8521- (CYViewController *) pageForPackage:(NSString *)name {
cb9c2100 8522 if (Package *package = [database_ packageWithName:name]) {
f441e717 8523 PackageController *view([self packageController]);
cb9c2100
JF
8524 [view setPackage:package];
8525 return view;
8526 } else {
6981ccdf
JF
8527 NSURL *url([NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"unknown" ofType:@"html"]]);
8528 url = [NSURL URLWithString:[[url absoluteString] stringByAppendingString:[NSString stringWithFormat:@"?%@", name]]];
9ae52960 8529 return [self _pageForURL:url withClass:[CYBrowserController class]];
cb9c2100
JF
8530 }
8531}
8532
881fe77f 8533- (CYViewController *) pageForURL:(NSURL *)url hasTag:(int *)tag {
cb9c2100 8534 if (tag != NULL)
84848968 8535 *tag = -1;
cb9c2100 8536
61b13cae
JF
8537 NSString *href([url absoluteString]);
8538 if ([href hasPrefix:@"apptapp://package/"])
8539 return [self pageForPackage:[href substringFromIndex:18]];
8540
2dfc46f7
JF
8541 NSString *scheme([[url scheme] lowercaseString]);
8542 if (![scheme isEqualToString:@"cydia"])
8543 return nil;
8544 NSString *path([url absoluteString]);
8545 if ([path length] < 8)
8546 return nil;
8547 path = [path substringFromIndex:8];
8548 if (![path hasPrefix:@"/"])
8549 path = [@"/" stringByAppendingString:path];
8550
8551 if ([path isEqualToString:@"/add-source"])
f441e717 8552 return [[[AddSourceController alloc] initWithDatabase:database_] autorelease];
2dfc46f7 8553 else if ([path isEqualToString:@"/storage"])
9ae52960 8554 return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[CYBrowserController class]];
2dfc46f7 8555 else if ([path isEqualToString:@"/sources"])
9ae52960 8556 return [[[SourceTable alloc] initWithDatabase:database_] autorelease];
2dfc46f7 8557 else if ([path isEqualToString:@"/packages"])
f441e717 8558 return [[[InstalledController alloc] initWithDatabase:database_] autorelease];
2dfc46f7 8559 else if ([path hasPrefix:@"/url/"])
9ae52960 8560 return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[CYBrowserController class]];
2dfc46f7
JF
8561 else if ([path hasPrefix:@"/launch/"])
8562 [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
8563 else if ([path hasPrefix:@"/package-settings/"])
f441e717 8564 return [[[SettingsController alloc] initWithDatabase:database_ package:[path substringFromIndex:18]] autorelease];
2dfc46f7 8565 else if ([path hasPrefix:@"/package-signature/"])
f441e717 8566 return [[[SignatureController alloc] initWithDatabase:database_ package:[path substringFromIndex:19]] autorelease];
2dfc46f7
JF
8567 else if ([path hasPrefix:@"/package/"])
8568 return [self pageForPackage:[path substringFromIndex:9]];
8569 else if ([path hasPrefix:@"/files/"]) {
8570 NSString *name = [path substringFromIndex:7];
cb9c2100
JF
8571
8572 if (Package *package = [database_ packageWithName:name]) {
9ae52960 8573 FileTable *files = [[[FileTable alloc] initWithDatabase:database_] autorelease];
cb9c2100
JF
8574 [files setPackage:package];
8575 return files;
8576 }
8577 }
8578
8579 return nil;
8580}
8581
8582- (void) applicationOpenURL:(NSURL *)url {
8583 [super applicationOpenURL:url];
8584 int tag;
881fe77f 8585 if (CYViewController *page = [self pageForURL:url hasTag:&tag]) {
cb9c2100 8586 [self setPage:page];
cb9c2100 8587 tag_ = tag;
9ae52960 8588 [tabbar_ setSelectedViewController:(tag_ == -1 ? nil : [[tabbar_ viewControllers] objectAtIndex:tag_])];
cb9c2100
JF
8589 }
8590}
8591
f99f86e2 8592- (void) applicationWillResignActive:(UIApplication *)application {
57e5156d 8593 // Stop refreshing if you get a phone call or lock the device.
96291f72
JF
8594 if ([container_ updating])
8595 [container_ cancelUpdate];
f99f86e2 8596
0a6531c7
GP
8597 if ([[self superclass] instancesRespondToSelector:@selector(applicationWillResignActive:)])
8598 [super applicationWillResignActive:application];
57e5156d
GP
8599}
8600
4cd1145e
GP
8601- (void) addStashController {
8602 stash_ = [[CYStashController alloc] init];
8603 [window_ addSubview:[stash_ view]];
8604}
8605
8606- (void) removeStashController {
8607 [[stash_ view] removeFromSuperview];
8608 [stash_ release];
8609}
8610
8611- (void) stash {
8612 [self setIdleTimerDisabled:YES];
8613
a8f3b357 8614 [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
4cd1145e
GP
8615 [self setStatusBarShowsProgress:YES];
8616 UpdateExternalStatus(1);
8617
8618 [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"];
8619
8620 UpdateExternalStatus(0);
8621 [self setStatusBarShowsProgress:NO];
8622
8623 [self removeStashController];
8624
8625 if (ExecFork() == 0) {
8626 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
8627 perror("launchctl stop");
8628 }
8629}
8630
7fc6909e
GP
8631- (void) setupTabBarController {
8632 tabbar_ = [[CYTabBarController alloc] initWithDatabase:database_];
8633 [tabbar_ setDelegate:self];
8634
8635 NSMutableArray *items([NSMutableArray arrayWithObjects:
8636 [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:kCydiaTag] autorelease],
8637 [[[UITabBarItem alloc] initWithTitle:UCLocalize("SECTIONS") image:[UIImage applicationImageNamed:@"install.png"] tag:kSectionsTag] autorelease],
8638 [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:kChangesTag] autorelease],
8639 [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:kSearchTag] autorelease],
8640 nil]);
8641
8642 if (IsWildcat_) {
8643 [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"source.png"] tag:kSourcesTag] autorelease] atIndex:3];
8644 [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:kInstalledTag] autorelease] atIndex:3];
8645 } else {
8646 [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("MANAGE") image:[UIImage applicationImageNamed:@"manage.png"] tag:kManageTag] autorelease] atIndex:3];
8647 }
8648
8649 NSMutableArray *controllers([NSMutableArray array]);
8650
8651 for (UITabBarItem *item in items) {
8652 CYNavigationController *controller([[[CYNavigationController alloc] initWithDatabase:database_] autorelease]);
8653 [controller setTabBarItem:item];
8654 [controllers addObject:controller];
8655 }
8656
8657 [tabbar_ setViewControllers:controllers];
7fc6909e
GP
8658}
8659
b26eb97d 8660- (void) applicationDidFinishLaunching:(id)unused {
9ae52960 8661 [CYBrowserController _initialize];
4825688a 8662
017b2b71
JF
8663 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
8664
5e563e79
JF
8665 Font12_ = [[UIFont systemFontOfSize:12] retain];
8666 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
8667 Font14_ = [[UIFont systemFontOfSize:14] retain];
8668 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
8669 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
8670
84848968 8671 tag_ = 0;
b26eb97d
JF
8672
8673 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
8674 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
8675
d3bef7bc
JF
8676 UIScreen *screen([UIScreen mainScreen]);
8677
8678 window_ = [[UIWindow alloc] initWithFrame:[screen bounds]];
5e563e79
JF
8679 [window_ orderFront:self];
8680 [window_ makeKey:self];
cb9c2100 8681 [window_ setHidden:NO];
d3bef7bc 8682
4cd1145e
GP
8683 if (
8684 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
8685 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
8686 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
8687 //readlink("/usr/bin", NULL, 0) == -1 && errno == EINVAL ||
8688 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
8689 readlink("/usr/lib/pam", NULL, 0) == -1 && errno == EINVAL ||
8690 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
8691 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL ||
8692 //readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL ||
8693 false
8694 ) {
8695 [self addStashController];
a591888a
JF
8696 // XXX: this would be much cleaner as a yieldToSelector:
8697 // that way the removeStashController could happen right here inline
8698 // we also could no longer require the useless stash_ field anymore
4cd1145e
GP
8699 [self performSelector:@selector(stash) withObject:nil afterDelay:0];
8700 return;
8701 }
8702
dbe0f181 8703 database_ = [Database sharedInstance];
017b2b71 8704
7fc6909e 8705 [self setupTabBarController];
e7a88a8c
GP
8706
8707 container_ = [[CYContainer alloc] initWithDatabase:database_];
e7a88a8c 8708 [container_ setUpdateDelegate:self];
1e4922b8 8709 [container_ setTabBarController:tabbar_];
e7a88a8c 8710 [window_ addSubview:[container_ view]];
6981ccdf 8711
525bc190 8712 // Show pinstripes while loading data.
c961857b 8713 [[container_ view] setBackgroundColor:[UIColor pinStripeColor]];
525bc190 8714
90e9a238 8715 [self performSelector:@selector(loadData) withObject:nil afterDelay:0];
d11754e5 8716_trace();
90e9a238
JF
8717}
8718
8719- (void) loadData {
d11754e5 8720_trace();
90e9a238
JF
8721 if (Role_ == nil) {
8722 [self showSettings];
8723 return;
8724 }
8725
7fc6909e 8726 [window_ setUserInteractionEnabled:NO];
6981ccdf 8727
11f75d4f 8728 UIView *container = [[[UIView alloc] init] autorelease];
7fc6909e
GP
8729 [container setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin];
8730
11f75d4f 8731 UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
7fc6909e
GP
8732 [spinner startAnimating];
8733 [container addSubview:spinner];
7fc6909e 8734
11f75d4f 8735 UILabel *label = [[[UILabel alloc] init] autorelease];
7fc6909e
GP
8736 [label setFont:[UIFont boldSystemFontOfSize:15.0f]];
8737 [label setBackgroundColor:[UIColor clearColor]];
8738 [label setTextColor:[UIColor blackColor]];
8739 [label setShadowColor:[UIColor whiteColor]];
8740 [label setShadowOffset:CGSizeMake(0, 1)];
835b451e 8741 [label setText:[NSString stringWithFormat:Elision_, UCLocalize("LOADING"), nil]];
7fc6909e 8742 [container addSubview:label];
7fc6909e
GP
8743
8744 CGSize viewsize = [[tabbar_ view] frame].size;
8745 CGSize spinnersize = [spinner bounds].size;
8746 CGSize textsize = [[label text] sizeWithFont:[label font]];
8747 float bothwidth = spinnersize.width + textsize.width + 5.0f;
8748
8749 CGRect containrect = {
8750 CGPointMake(floorf((viewsize.width / 2) - (bothwidth / 2)), floorf((viewsize.height / 2) - (spinnersize.height / 2))),
8751 CGSizeMake(bothwidth, spinnersize.height)
8752 };
8753 CGRect textrect = {
8754 CGPointMake(spinnersize.width + 5.0f, floorf((spinnersize.height / 2) - (textsize.height / 2))),
8755 textsize
8756 };
8757 CGRect spinrect = {
8758 CGPointZero,
8759 spinnersize
8760 };
8761
8762 [container setFrame:containrect];
8763 [spinner setFrame:spinrect];
8764 [label setFrame:textrect];
8765 [[container_ view] addSubview:container];
7fc6909e
GP
8766
8767 [self reloadData];
6981ccdf
JF
8768 PrintTimes();
8769
7fc6909e 8770 // Show the home page
c09b1ba1 8771 [tabbar_ setSelectedIndex:0];
6981ccdf 8772 _setHomePage(self);
7fc6909e 8773 [window_ setUserInteractionEnabled:YES];
525bc190
GP
8774
8775 // XXX: does this actually slow anything down?
8776 [[container_ view] setBackgroundColor:[UIColor clearColor]];
7fc6909e 8777 [container removeFromSuperview];
b26eb97d
JF
8778}
8779
9ae52960 8780- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {
1ca35d78 8781 if (item != nil && IsWildcat_) {
9ae52960 8782 [sheet showFromBarButtonItem:item animated:YES];
1ca35d78
GP
8783 } else {
8784 [sheet showInView:window_];
8785 }
e057ec05
JF
8786}
8787
a75f53e7
JF
8788@end
8789
686e302f
JF
8790/*IMP alloc_;
8791id Alloc_(id self, SEL selector) {
8792 id object = alloc_(self, selector);
cb9c2100 8793 lprintf("[%s]A-%p\n", self->isa->name, object);
686e302f
JF
8794 return object;
8795}*/
8796
e057ec05
JF
8797/*IMP dealloc_;
8798id Dealloc_(id self, SEL selector) {
8799 id object = dealloc_(self, selector);
cb9c2100 8800 lprintf("[%s]D-%p\n", self->isa->name, object);
e057ec05
JF
8801 return object;
8802}*/
686e302f 8803
6932575e
JF
8804Class $WebDefaultUIKitDelegate;
8805
c59881cd 8806MSHook(void, UIWebDocumentView$_setUIKitDelegate$, UIWebDocumentView *self, SEL _cmd, id delegate) {
6932575e
JF
8807 if (delegate == nil && $WebDefaultUIKitDelegate != nil)
8808 delegate = [$WebDefaultUIKitDelegate sharedUIKitDelegate];
c59881cd 8809 return _UIWebDocumentView$_setUIKitDelegate$(self, _cmd, delegate);
6932575e
JF
8810}
8811
aaab313d
RP
8812static NSNumber *shouldPlayKeyboardSounds;
8813
8814Class $UIHardware;
8815
d5f224e7
JF
8816MSHook(void, UIHardware$_playSystemSound$, Class self, SEL _cmd, int sound) {
8817 switch (sound) {
aaab313d
RP
8818 case 1104: // Keyboard Button Clicked
8819 case 1105: // Keyboard Delete Repeated
d5f224e7
JF
8820 if (shouldPlayKeyboardSounds == nil) {
8821 NSDictionary *dict([[[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.preferences.sounds.plist"] autorelease]);
8822 shouldPlayKeyboardSounds = [([dict objectForKey:@"keyboard"] ?: (id) kCFBooleanTrue) retain];
aaab313d 8823 }
d5f224e7 8824
aaab313d
RP
8825 if (![shouldPlayKeyboardSounds boolValue])
8826 break;
d5f224e7 8827
aaab313d 8828 default:
d5f224e7 8829 _UIHardware$_playSystemSound$(self, _cmd, sound);
aaab313d
RP
8830 }
8831}
8832
f464053e 8833int main(int argc, char *argv[]) { _pooled
7ec29c77 8834 _trace();
6932575e 8835
5ec44e34
JF
8836 if (Class $UIDevice = objc_getClass("UIDevice")) {
8837 UIDevice *device([$UIDevice currentDevice]);
8838 IsWildcat_ = [device respondsToSelector:@selector(isWildcat)] && [device isWildcat];
8839 } else
8840 IsWildcat_ = false;
8841
dd9390c5 8842 PackageName = reinterpret_cast<CYString &(*)(Package *, SEL)>(method_getImplementation(class_getInstanceMethod([Package class], @selector(cyname))));
43b742af 8843
3e3977a2
JF
8844 /* Library Hacks {{{ */
8845 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
8846
8847 $WebDefaultUIKitDelegate = objc_getClass("WebDefaultUIKitDelegate");
8848 Method UIWebDocumentView$_setUIKitDelegate$(class_getInstanceMethod([WebView class], @selector(_setUIKitDelegate:)));
8849 if (UIWebDocumentView$_setUIKitDelegate$ != NULL) {
8850 _UIWebDocumentView$_setUIKitDelegate$ = reinterpret_cast<void (*)(UIWebDocumentView *, SEL, id)>(method_getImplementation(UIWebDocumentView$_setUIKitDelegate$));
8851 method_setImplementation(UIWebDocumentView$_setUIKitDelegate$, reinterpret_cast<IMP>(&$UIWebDocumentView$_setUIKitDelegate$));
8852 }
aaab313d
RP
8853
8854 $UIHardware = objc_getClass("UIHardware");
8855 Method UIHardware$_playSystemSound$(class_getClassMethod($UIHardware, @selector(_playSystemSound:)));
8856 if (UIHardware$_playSystemSound$ != NULL) {
8857 _UIHardware$_playSystemSound$ = reinterpret_cast<void (*)(Class, SEL, int)>(method_getImplementation(UIHardware$_playSystemSound$));
8858 method_setImplementation(UIHardware$_playSystemSound$, reinterpret_cast<IMP>(&$UIHardware$_playSystemSound$));
8859 }
3e3977a2
JF
8860 /* }}} */
8861 /* Set Locale {{{ */
6932575e 8862 Locale_ = CFLocaleCopyCurrent();
bb9edf8b
JF
8863 Languages_ = [NSLocale preferredLanguages];
8864 //CFStringRef locale(CFLocaleGetIdentifier(Locale_));
f7ee98cb 8865 //NSLog(@"%@", [Languages_ description]);
85e0f0f0 8866
bb9edf8b
JF
8867 const char *lang;
8868 if (Languages_ == nil || [Languages_ count] == 0)
85e0f0f0 8869 // XXX: consider just setting to C and then falling through?
bb9edf8b 8870 lang = NULL;
85e0f0f0 8871 else {
bb9edf8b 8872 lang = [[Languages_ objectAtIndex:0] UTF8String];
85e0f0f0
JF
8873 setenv("LANG", lang, true);
8874 }
8875
bb9edf8b
JF
8876 //std::setlocale(LC_ALL, lang);
8877 NSLog(@"Setting Language: %s", lang);
3e3977a2 8878 /* }}} */
6932575e 8879
c59881cd 8880 apr_app_initialize(&argc, const_cast<const char * const **>(&argv), NULL);
6932575e 8881
3e3977a2 8882 /* Parse Arguments {{{ */
64da7a2f
JF
8883 bool substrate(false);
8884
8885 if (argc != 0) {
8886 char **args(argv);
8887 int arge(1);
8888
8889 for (int argi(1); argi != argc; ++argi)
8890 if (strcmp(argv[argi], "--") == 0) {
8891 arge = argi;
8892 argv[argi] = argv[0];
8893 argv += argi;
8894 argc -= argi;
8895 break;
8896 }
8897
8898 for (int argi(1); argi != arge; ++argi)
c59881cd 8899 if (strcmp(args[argi], "--substrate") == 0)
64da7a2f
JF
8900 substrate = true;
8901 else
8902 fprintf(stderr, "unknown argument: %s\n", args[argi]);
8903 }
3e3977a2 8904 /* }}} */
0039464f 8905
3e3977a2
JF
8906 App_ = [[NSBundle mainBundle] bundlePath];
8907 Home_ = NSHomeDirectory();
c59881cd 8908 Advanced_ = YES;
3e3977a2 8909
686e302f
JF
8910 setuid(0);
8911 setgid(0);
8912
8913 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
8914 alloc_ = alloc->method_imp;
8915 alloc->method_imp = (IMP) &Alloc_;*/
8916
e057ec05
JF
8917 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
8918 dealloc_ = dealloc->method_imp;
8919 dealloc->method_imp = (IMP) &Dealloc_;*/
8920
c59881cd 8921 /* System Information {{{ */
2d28b35a 8922 size_t size;
cb9c2100
JF
8923
8924 int maxproc;
8925 size = sizeof(maxproc);
8926 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
8927 perror("sysctlbyname(\"kern.maxproc\", ?)");
8928 else if (maxproc < 64) {
8929 maxproc = 64;
8930 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
8931 perror("sysctlbyname(\"kern.maxproc\", #)");
8932 }
8933
017b2b71
JF
8934 sysctlbyname("kern.osversion", NULL, &size, NULL, 0);
8935 char *osversion = new char[size];
8936 if (sysctlbyname("kern.osversion", osversion, &size, NULL, 0) == -1)
8937 perror("sysctlbyname(\"kern.osversion\", ?)");
8938 else
8939 System_ = [NSString stringWithUTF8String:osversion];
8940
2d28b35a
JF
8941 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
8942 char *machine = new char[size];
cb9c2100
JF
8943 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
8944 perror("sysctlbyname(\"hw.machine\", ?)");
8945 else
8946 Machine_ = machine;
2d28b35a 8947
6188cfdd
JF
8948 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice")) {
8949 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
8950 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
017b2b71 8951 SerialNumber_ = [NSString stringWithString:(NSString *)serial];
6188cfdd
JF
8952 CFRelease(serial);
8953 }
8954
8955 if (CFTypeRef ecid = IORegistryEntrySearchCFProperty(service, kIODeviceTreePlane, CFSTR("unique-chip-id"), kCFAllocatorDefault, kIORegistryIterateRecursively)) {
8956 NSData *data((NSData *) ecid);
8957 size_t length([data length]);
8958 uint8_t bytes[length];
8959 [data getBytes:bytes];
8960 char string[length * 2 + 1];
8961 for (size_t i(0); i != length; ++i)
8962 sprintf(string + i * 2, "%.2X", bytes[length - i - 1]);
017b2b71 8963 ChipID_ = [NSString stringWithUTF8String:string];
6188cfdd
JF
8964 CFRelease(ecid);
8965 }
8966
8967 IOObjectRelease(service);
8968 }
8969 }
8970
2cb68ddf 8971 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
2d28b35a 8972
3074466a
JF
8973 CFStringRef (*$CTSIMSupportCopyMobileSubscriberCountryCode)(CFAllocatorRef);
8974 $CTSIMSupportCopyMobileSubscriberCountryCode = reinterpret_cast<CFStringRef (*)(CFAllocatorRef)>(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode"));
8975 CFStringRef mcc($CTSIMSupportCopyMobileSubscriberCountryCode == NULL ? NULL : (*$CTSIMSupportCopyMobileSubscriberCountryCode)(kCFAllocatorDefault));
8976
8977 CFStringRef (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(CFAllocatorRef);
8978 $CTSIMSupportCopyMobileSubscriberNetworkCode = reinterpret_cast<CFStringRef (*)(CFAllocatorRef)>(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode"));
8979 CFStringRef mnc($CTSIMSupportCopyMobileSubscriberNetworkCode == NULL ? NULL : (*$CTSIMSupportCopyMobileSubscriberNetworkCode)(kCFAllocatorDefault));
8980
8981 if (mcc != NULL && mnc != NULL)
8982 PLMN_ = [NSString stringWithFormat:@"%@%@", mcc, mnc];
8983
8984 if (mnc != NULL)
8985 CFRelease(mnc);
8986 if (mcc != NULL)
8987 CFRelease(mcc);
8988
68c05606
JF
8989 if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
8990 Build_ = [system objectForKey:@"ProductBuildVersion"];
7b00c562
JF
8991 if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
8992 Product_ = [info objectForKey:@"SafariProductVersion"];
8993 Safari_ = [info objectForKey:@"CFBundleVersion"];
8994 }
c59881cd 8995 /* }}} */
3e3977a2 8996 /* Load Database {{{ */
7ec29c77 8997 _trace();
6932575e
JF
8998 Metadata_ = [[[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"] autorelease];
8999 _trace();
9000 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7ec29c77
JF
9001
9002 if (Metadata_ == NULL)
6932575e 9003 Metadata_ = [NSMutableDictionary dictionaryWithCapacity:2];
fa7bb92f 9004 else {
ca45de84 9005 Settings_ = [Metadata_ objectForKey:@"Settings"];
faf4eb4f 9006
686e302f 9007 Packages_ = [Metadata_ objectForKey:@"Packages"];
fa7bb92f 9008 Sections_ = [Metadata_ objectForKey:@"Sections"];
faf4eb4f 9009 Sources_ = [Metadata_ objectForKey:@"Sources"];
37455cf8
JF
9010
9011 Token_ = [Metadata_ objectForKey:@"Token"];
faf4eb4f
JF
9012 }
9013
9014 if (Settings_ != nil)
9015 Role_ = [Settings_ objectForKey:@"Role"];
9016
9017 if (Packages_ == nil) {
9018 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
9019 [Metadata_ setObject:Packages_ forKey:@"Packages"];
9020 }
9021
9022 if (Sections_ == nil) {
9023 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
9024 [Metadata_ setObject:Sections_ forKey:@"Sections"];
9025 }
9026
9027 if (Sources_ == nil) {
9028 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
9029 [Metadata_ setObject:Sources_ forKey:@"Sources"];
fa7bb92f 9030 }
3e3977a2 9031 /* }}} */
686e302f 9032
c59881cd
JF
9033 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
9034
5ec44e34
JF
9035 if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", F_OK) == 0)
9036 dlopen("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", RTLD_LAZY | RTLD_GLOBAL);
8944281a
JF
9037 if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
9038 dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
9039 /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
9040 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
1eb0c554 9041
5ec44e34
JF
9042 int version([[NSString stringWithContentsOfFile:@"/var/lib/cydia/firmware.ver"] intValue]);
9043
17f91215 9044 if (access("/tmp/.cydia.fw", F_OK) == 0) {
4825688a 9045 unlink("/tmp/.cydia.fw");
17f91215 9046 goto firmware;
daf7f6e2 9047 } else if (access("/User", F_OK) != 0 || version < 2) {
17f91215 9048 firmware:
7ec29c77 9049 _trace();
6a0db335 9050 system("/usr/libexec/cydia/firmware.sh");
7ec29c77
JF
9051 _trace();
9052 }
63a1e4b8 9053
2cb68ddf
JF
9054 _assert([[NSFileManager defaultManager]
9055 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
9056 withIntermediateDirectories:YES
9057 attributes:nil
9058 error:NULL
9059 ]);
9060
3e3977a2
JF
9061 if (access("/tmp/cydia.chk", F_OK) == 0) {
9062 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
9063 _assert(errno == ENOENT);
9064 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
9065 _assert(errno == ENOENT);
9066 }
9067
6188cfdd 9068 /* APT Initialization {{{ */
bb9edf8b
JF
9069 _assert(pkgInitConfig(*_config));
9070 _assert(pkgInitSystem(*_config, _system));
9071
9072 if (lang != NULL)
9073 _config->Set("APT::Acquire::Translation", lang);
69caeecb
JF
9074
9075 // XXX: this timeout might be important :(
9076 //_config->Set("Acquire::http::Timeout", 15);
9077
419a9efd 9078 _config->Set("Acquire::http::MaxParallel", 3);
6188cfdd 9079 /* }}} */
3e3977a2 9080 /* Color Choices {{{ */
e057ec05
JF
9081 space_ = CGColorSpaceCreateDeviceRGB();
9082
5e563e79 9083 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
d72d91aa 9084 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
e057ec05 9085 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
87c76914 9086 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
e057ec05 9087 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
faf4eb4f 9088 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
d8d9a65c
JF
9089 Green_.Set(space_, 0.0, 0.5, 0.0, 1.0);
9090 Purple_.Set(space_, 0.0, 0.0, 0.7, 1.0);
9091 Purplish_.Set(space_, 0.4, 0.4, 0.8, 1.0);
6188cfdd 9092
3ff1504e
JF
9093 InstallingColor_ = [UIColor colorWithRed:0.88f green:1.00f blue:0.88f alpha:1.00f];
9094 RemovingColor_ = [UIColor colorWithRed:1.00f green:0.88f blue:0.88f alpha:1.00f];
3e3977a2 9095 /* }}}*/
3e3977a2 9096 /* UIKit Configuration {{{ */
6932575e
JF
9097 void (*$GSFontSetUseLegacyFontMetrics)(BOOL)(reinterpret_cast<void (*)(BOOL)>(dlsym(RTLD_DEFAULT, "GSFontSetUseLegacyFontMetrics")));
9098 if ($GSFontSetUseLegacyFontMetrics != NULL)
9099 $GSFontSetUseLegacyFontMetrics(YES);
faf4eb4f 9100
d4f84362
JF
9101 // XXX: I have a feeling this was important
9102 //UIKeyboardDisableAutomaticAppearance();
3e3977a2 9103 /* }}} */
2cb68ddf 9104
6981ccdf 9105 Colon_ = UCLocalize("COLON_DELIMITED");
835b451e 9106 Elision_ = UCLocalize("ELISION");
6981ccdf
JF
9107 Error_ = UCLocalize("ERROR");
9108 Warning_ = UCLocalize("WARNING");
9109
7ec29c77 9110 _trace();
bf50bdfa 9111 int value(UIApplicationMain(argc, argv, @"Cydia", @"Cydia"));
e057ec05
JF
9112
9113 CGColorSpaceRelease(space_);
b27f0a94 9114 CFRelease(Locale_);
e057ec05 9115
e057ec05 9116 return value;
2938b930 9117}