/* Cydia - iPhone UIKit Front-End for Debian APT
- * Copyright (C) 2008-2014 Jay Freeman (saurik)
+ * Copyright (C) 2008-2015 Jay Freeman (saurik)
*/
/* GNU General Public License, Version 3 {{{ */
#include <WebKit/DOMHTMLIFrameElement.h>
#include <algorithm>
+#include <fstream>
#include <iomanip>
#include <set>
#include <sstream>
#include <string>
-#include <ext/stdio_filebuf.h>
+#include "fdstream.hpp"
#undef ABS
+#include "apt.h"
#include <apt-pkg/acquire.h>
#include <apt-pkg/acquire-item.h>
#include <apt-pkg/algorithms.h>
#include <sys/mount.h>
#include <sys/reboot.h>
+#include <dirent.h>
#include <fcntl.h>
#include <notify.h>
#include <dlfcn.h>
};
// }}}
-static void setreugid(uid_t uid, gid_t gid) {
- _assert(setreuid(uid, uid) != -1);
- _assert(setregid(gid, gid) != -1);
-}
-
-static void setreguid(gid_t gid, uid_t uid) {
- _assert(setregid(gid, gid) != -1);
- _assert(setreuid(uid, uid) != -1);
-}
-
-struct Root {
- Root() {
- _trace();
- setreugid(0, 0);
- _assert(pthread_setugid_np(0, 0) != -1);
- setreguid(501, 501);
- }
-
- ~Root() {
- _trace();
- setreugid(0, 0);
- _assert(pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE) != -1);
- setreguid(501, 501);
- }
-};
-
-#define _root(code) \
- ({ Root _root; code; })
+@implementation NSDictionary (Cydia)
+- (id) invokeUndefinedMethodFromWebScript:(NSString *)name withArguments:(NSArray *)arguments {
+ if (false);
+ else if ([name isEqualToString:@"get"])
+ return [self objectForKey:[arguments objectAtIndex:0]];
+ else if ([name isEqualToString:@"keys"])
+ return [self allKeys];
+ return nil;
+} @end
static NSString *Colon_;
NSString *Elision_;
[NSString stringWithFormat:@"%@/%s", Cache_, file]
static void (*$SBSSetInterceptsMenuButtonForever)(bool);
+static NSData *(*$SBSCopyIconImagePNGDataForDisplayIdentifier)(NSString *);
static CFStringRef (*$MGCopyAnswer)(CFStringRef);
return [[NSString stringWithUTF8String:page] stringByAppendingString:path];
}
-static void ReapZombie(pid_t pid) {
- int status;
- wait:
- if (waitpid(pid, &status, 0) == -1)
- if (errno == EINTR)
- goto wait;
- else _assert(false);
+static NSString *ShellEscape(NSString *value) {
+ return [NSString stringWithFormat:@"'%@'", [value stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]];
}
static _finline void UpdateExternalStatus(uint64_t newStatus) {
CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(data), size, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull);
}
+static _finline CFStringRef CYStringCreate(const std::string &data) {
+ return CYStringCreate(data.data(), data.size());
+}
+
static _finline CFStringRef CYStringCreate(const char *data) {
return CYStringCreate(data, strlen(data));
}
static int Finish_;
static bool RestartSubstrate_;
-static bool UpgradeCydia_;
static NSArray *Finishes_;
#define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
return [NSString stringWithUTF8String:string];
}
+static NSString *VerifySource(NSString *href) {
+ static RegEx href_r("(http(s?)://|file:///)[^# ]*");
+ if (!href_r(href)) {
+ [[[[UIAlertView alloc]
+ initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("INVALID_URL")]
+ message:UCLocalize("INVALID_URL_EX")
+ delegate:nil
+ cancelButtonTitle:UCLocalize("OK")
+ otherButtonTitles:nil
+ ] autorelease] show];
+
+ return nil;
+ }
+
+ if (![href hasSuffix:@"/"])
+ href = [href stringByAppendingString:@"/"];
+ return href;
+}
+
@class Cydia;
/* Delegate Prototypes {{{ */
- (void) _saveConfig;
- (void) syncData;
- (void) addSource:(NSDictionary *)source;
-- (void) addTrivialSource:(NSString *)href;
+- (BOOL) addTrivialSource:(NSString *)href;
- (UIProgressHUD *) addProgressHUD;
- (void) removeProgressHUD:(UIProgressHUD *)hud;
- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item;
+ (Database *) sharedInstance;
- (unsigned) era;
+- (bool) hasPackages;
- (void) _readCydia:(NSNumber *)fd;
- (void) _readStatus:(NSNumber *)fd;
char version_[8];
char name_[];
-};
+} _packed;
struct MetaValue :
Cytore::Block
{
uint32_t active_;
Cytore::Offset<PackageValue> packages_[1 << 16];
-};
+} _packed;
static Cytore::File<MetaValue> MetaFile_;
// }}}
- (NSString *) installed;
- (BOOL) uninstalled;
-- (BOOL) valid;
- (BOOL) upgradableAndEssential:(BOOL)essential;
- (BOOL) essential;
- (BOOL) broken;
_end
_profile(Package$parse$Tagline)
- const char *start, *end;
- if (parser->ShortDesc(start, end)) {
- const char *stop(reinterpret_cast<const char *>(memchr(start, '\n', end - start)));
- if (stop == NULL)
- stop = end;
- while (stop != start && stop[-1] == '\r')
- --stop;
- parsed->tagline_.set(pool_, start, stop - start);
- }
+ parsed->tagline_.set(pool_, parser->ShortDesc());
_end
_profile(Package$parse$Retain)
version_ = version;
- pkgCache::PkgIterator iterator(version.ParentPkg());
+ pkgCache::PkgIterator iterator(version_.ParentPkg());
iterator_ = iterator;
_profile(Package$initWithVersion$Version)
- if (!version_.end())
- file_ = version_.FileList();
- else {
- pkgCache &cache([database_ cache]);
- file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
- }
+ file_ = version_.FileList();
_end
_profile(Package$initWithVersion$Cache)
- name_.set(NULL, iterator.Display());
+ name_.set(NULL, version_.Display());
latest_.set(NULL, StripVersion_(version_.VerStr()));
} while (false); _end
_profile(Package$initWithVersion$Tags)
+#ifdef __arm64__
+ pkgCache::TagIterator tag(version_.TagList());
+#else
pkgCache::TagIterator tag(iterator.TagList());
+#endif
if (!tag.end()) {
tags_ = [NSMutableArray arrayWithCapacity:8];
return iterator_;
}
+- (NSArray *) downgrades {
+ NSMutableArray *versions([NSMutableArray arrayWithCapacity:4]);
+
+ for (auto version(iterator_.VersionList()); !version.end(); ++version) {
+ if (version == version_)
+ continue;
+ Package *package([[[Package allocWithZone:NULL] initWithVersion:version withZone:NULL inPool:NULL database:database_] autorelease]);
+ if ([package source] == nil)
+ continue;
+ [versions addObject:package];
+ }
+
+ return versions;
+}
+
- (NSString *) section {
if (section$_ == nil) {
if (section_ == NULL)
}
- (NSString *) longSection {
- return LocalizeSection([self section]);
+ if (NSString *section = [self section])
+ return LocalizeSection(section);
+ else
+ return nil;
}
- (NSString *) shortSection {
@synchronized (database_) {
pkgRecords::Parser &parser([database_ records]->Lookup(file_));
-
- const char *start, *end;
- if (!parser.ShortDesc(start, end))
+ std::string value(parser.ShortDesc());
+ if (value.empty())
return nil;
-
- if (end - start > 200)
- end = start + 200;
-
- /*
- if (const char *stop = reinterpret_cast<const char *>(memchr(start, '\n', end - start)))
- end = stop;
-
- while (end != start && end[-1] == '\r')
- --end;
- */
-
- return [(id) CYStringCreate(start, end - start) autorelease];
+ if (value.size() > 200)
+ value.resize(200);
+ return [(id) CYStringCreate(value) autorelease];
} }
- (unichar) index {
return installed_.empty();
}
-- (BOOL) valid {
- return !version_.end();
-}
-
- (BOOL) upgradableAndEssential:(BOOL)essential {
_profile(Package$upgradableAndEssential)
pkgCache::VerIterator current(iterator_.CurrentVer());
if (current.end())
return essential && essential_;
else
- return !version_.end() && version_ != current;
+ return version_ != current;
_end
}
for (NSString *file in files)
if (application_r(file)) {
NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
+ if (info == nil)
+ continue;
NSString *id([info objectForKey:@"CFBundleIdentifier"]);
- if ([id isEqualToString:me])
+ if (id == nil || [id isEqualToString:me])
continue;
NSString *display([info objectForKey:@"CFBundleDisplayName"]);
- (void) clear {
@synchronized (database_) {
+ if ([database_ era] != era_ || file_.end())
+ return;
+
pkgProblemResolver *resolver = [database_ resolver];
resolver->Clear(iterator_);
- (void) install {
@synchronized (database_) {
+ if ([database_ era] != era_ || file_.end())
+ return;
+
pkgProblemResolver *resolver = [database_ resolver];
resolver->Clear(iterator_);
resolver->Protect(iterator_);
pkgCacheFile &cache([database_ cache]);
+ cache->SetCandidateVersion(version_);
cache->SetReInstall(iterator_, false);
cache->MarkInstall(iterator_, false);
- (void) remove {
@synchronized (database_) {
+ if ([database_ era] != era_ || file_.end())
+ return;
+
pkgProblemResolver *resolver = [database_ resolver];
resolver->Clear(iterator_);
resolver->Remove(iterator_);
CFArrayRemoveAllValues(packages_);
}
+- (bool) hasPackages {
+ return CFArrayGetCount(packages_) != 0;
+}
+
- (void) dealloc {
// XXX: actually implement this thing
_assert(false);
}
- (void) _readCydia:(NSNumber *)fd {
- __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
- std::istream is(&ib);
+ boost::fdistream is([fd intValue]);
std::string line;
static RegEx finish_r("finish:([^:]*)");
}
- (void) _readStatus:(NSNumber *)fd {
- __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
- std::istream is(&ib);
+ boost::fdistream is([fd intValue]);
std::string line;
static RegEx conffile_r("status: [^ ]* : conffile-prompt : (.*?) *");
}
- (void) _readOutput:(NSNumber *)fd {
- __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
- std::istream is(&ib);
+ boost::fdistream is([fd intValue]);
std::string line;
while (std::getline(is, line)) {
@synchronized (self) {
if (static_cast<pkgDepCache *>(cache_) == NULL)
return nil;
- pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
+ pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]
+#ifdef __arm64__
+ , "any"
+#endif
+ ));
return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:NULL database:self];
} }
- (bool) popErrorWithTitle:(NSString *)title forReadList:(pkgSourceList &)list {
if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
return true;
- if ([self popErrorWithTitle:title forOperation:list.Read(SOURCES_LIST)])
- return true;
return false;
+
+ list.Reset();
+
+ bool error(false);
+
+ if (access("/etc/apt/sources.list", F_OK) == 0)
+ error |= [self popErrorWithTitle:title forOperation:list.ReadAppend("/etc/apt/sources.list")];
+
+ std::string base("/etc/apt/sources.list.d");
+ if (DIR *sources = opendir(base.c_str())) {
+ while (dirent *source = readdir(sources))
+ if (source->d_name[0] != '.' && source->d_namlen > 5 && strcmp(source->d_name + source->d_namlen - 5, ".list") == 0 && strcmp(source->d_name, "cydia.list") != 0)
+ error |= [self popErrorWithTitle:title forOperation:list.ReadAppend((base + "/" + source->d_name).c_str())];
+ closedir(sources);
+ }
+
+ error |= [self popErrorWithTitle:title forOperation:list.ReadAppend(SOURCES_LIST)];
+
+ return error;
}
- (void) reloadDataWithInvocation:(NSInvocation *)invocation {
}
_end
- delock_ = GetStatusDate();
-
_trace();
OpProgress progress;
bool opened;
open:
+ delock_ = GetStatusDate();
_profile(reloadDataWithInvocation$pkgCacheFile)
opened = cache_.Open(progress, false);
_end
if (!opened) {
- // XXX: what if there are errors, but Open() == true? this should be merged with popError:
+ // XXX: this block should probably be merged with popError: in some way
while (!_error->empty()) {
std::string error;
bool warning(!_error->PopMessage(error));
}
return;
- }
+ } else if ([self popErrorWithTitle:title forOperation:true])
+ return;
_trace();
unlink("/tmp/cydia.chk");
delock_ = nil;
pkgPackageManager::OrderResult result(manager_->DoInstall(statusfd_));
+
+ NSString *oextended(@"/var/lib/apt/extended_states");
+ NSString *nextended(Cache("extended_states"));
+
+ struct stat info;
+ if (stat([nextended UTF8String], &info) != -1 && (info.st_mode & S_IFMT) == S_IFREG)
+ system([[NSString stringWithFormat:@"/usr/libexec/cydia/cydo /bin/cp --remove-destination %@ %@", ShellEscape(nextended), ShellEscape(oextended)] UTF8String]);
+
+ unlink([nextended UTF8String]);
+ symlink([oextended UTF8String], [nextended UTF8String]);
+
if ([self popErrorWithTitle:title])
return;
+ (NSArray *) _attributeKeys {
return [NSArray arrayWithObjects:
+ @"bittage",
@"bbsnum",
@"build",
+ @"cells",
@"coreFoundationVersionNumber",
@"device",
@"ecid",
return Cydia_;
}
+- (unsigned) bittage {
+#if 0
+#elif defined(__arm64__)
+ return 64;
+#elif defined(__arm__)
+ return 32;
+#else
+ return 0;
+#endif
+}
+
- (NSString *) build {
return System_;
}
return (id) Idiom_ ?: [NSNull null];
}
+- (NSArray *) cells {
+ auto *$_CTServerConnectionCreate(reinterpret_cast<id (*)(void *, void *, void *)>(dlsym(RTLD_DEFAULT, "_CTServerConnectionCreate")));
+ if ($_CTServerConnectionCreate == NULL)
+ return nil;
+
+ struct CTResult { int flag; int error; };
+ auto *$_CTServerConnectionCellMonitorCopyCellInfo(reinterpret_cast<CTResult (*)(CFTypeRef, void *, CFArrayRef *)>(dlsym(RTLD_DEFAULT, "_CTServerConnectionCellMonitorCopyCellInfo")));
+ if ($_CTServerConnectionCellMonitorCopyCellInfo == NULL)
+ return nil;
+
+ _H<const void> connection($_CTServerConnectionCreate(NULL, NULL, NULL), true);
+ if (connection == nil)
+ return nil;
+
+ int count(0);
+ CFArrayRef cells(NULL);
+ auto result($_CTServerConnectionCellMonitorCopyCellInfo(connection, &count, &cells));
+ if (result.flag != 0)
+ return nil;
+
+ return [(NSArray *) cells autorelease];
+}
+
- (NSString *) mcc {
if (CFStringRef (*$CTSIMSupportCopyMobileSubscriberCountryCode)(CFAllocatorRef) = reinterpret_cast<CFStringRef (*)(CFAllocatorRef)>(dlsym(RTLD_DEFAULT, "CTSIMSupportCopyMobileSubscriberCountryCode")))
return [(NSString *) (*$CTSIMSupportCopyMobileSubscriberCountryCode)(kCFAllocatorDefault) autorelease];
return @"getAllSources";
else if (selector == @selector(getApplicationInfo:value:))
return @"getApplicationInfoValue";
+ else if (selector == @selector(getDisplayIdentifiers))
+ return @"getDisplayIdentifiers";
+ else if (selector == @selector(getLocalizedNameForDisplayIdentifier:))
+ return @"getLocalizedNameForDisplayIdentifier";
else if (selector == @selector(getKernelNumber:))
return @"getKernelNumber";
else if (selector == @selector(getKernelString:))
return [info objectForKey:key];
}
+- (NSArray *) getDisplayIdentifiers {
+ return SBSCopyApplicationDisplayIdentifiers(false, false);
+}
+
+- (NSString *) getLocalizedNameForDisplayIdentifier:(NSString *)identifier {
+ return [SBSCopyLocalizedApplicationNameForDisplayIdentifier(identifier) autorelease] ?: (id) [NSNull null];
+}
+
- (NSNumber *) getKernelNumber:(NSString *)name {
const char *string([name UTF8String]);
nil] waitUntilDone:NO];
}
-- (void) addTrivialSource:(NSString *)href {
+- (BOOL) addTrivialSource:(NSString *)href {
+ href = VerifySource(href);
+ if (href == nil)
+ return NO;
[delegate_ performSelectorOnMainThread:@selector(addTrivialSource:) withObject:href waitUntilDone:NO];
+ return YES;
}
- (void) refreshSources {
nil];
}
-ssize_t DiskUsage(const char *path);
-
- (NSNumber *) du:(NSString *)path {
- ssize_t usage(DiskUsage([path UTF8String]));
- if (usage != -1)
- usage /= 1024;
- return [NSNumber numberWithUnsignedLong:usage];
+ NSNumber *value(nil);
+
+ FILE *du(popen([[NSString stringWithFormat:@"/usr/libexec/cydia/cydo /usr/libexec/cydia/du -ks %@", ShellEscape(path)] UTF8String], "r"));
+ if (du != NULL) {
+ char line[1024];
+ while (fgets(line, sizeof(line), du) != NULL) {
+ size_t length(strlen(line));
+ while (length != 0 && line[length - 1] == '\n')
+ line[--length] = '\0';
+ if (char *tab = strchr(line, '\t')) {
+ *tab = '\0';
+ value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)];
+ }
+ }
+ pclose(du);
+ }
+
+ return value;
}
- (void) close {
}
- (NSString *) substitutePackageNames:(NSString *)message {
+ auto database([Database sharedInstance]);
+
+ // XXX: this check is less racy than you'd expect, but this entire concept is a little awkward
+ if ([database hasPackages])
+ return message;
+
NSMutableArray *words([[[message componentsSeparatedByString:@" "] mutableCopy] autorelease]);
for (size_t i(0), e([words count]); i != e; ++i) {
NSString *word([words objectAtIndex:i]);
- if (Package *package = [[Database sharedInstance] packageWithName:word])
+ if (Package *package = [database packageWithName:word])
[words replaceObjectAtIndex:i withObject:[package name]];
}
issues_ = [NSMutableArray arrayWithCapacity:4];
- UpgradeCydia_ = false;
-
for (Package *package in packages) {
pkgCache::PkgIterator iterator([package iterator]);
NSString *name([package id]);
[removes addObject:name];
}
- if ([name isEqualToString:@"cydia"])
- UpgradeCydia_ = true;
-
substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
substrate_ |= DepSubstrate(iterator.CurrentVer());
}
[super viewWillAppear:animated];
}
-- (void) reloadSpringBoard {
- if (kCFCoreFoundationVersionNumber > 700) { // XXX: iOS 6.x
- system("/bin/launchctl stop com.apple.backboardd");
- sleep(15);
- system("/usr/bin/killall backboardd SpringBoard sbreload");
- return;
- }
-
- pid_t pid(ExecFork());
- if (pid == 0) {
- if (setsid() == -1)
- perror("setsid");
-
- pid_t pid(ExecFork());
- if (pid == 0) {
- execl("/usr/libexec/cydia/cydo", "cydo", "/usr/bin/sbreload", NULL);
- perror("sbreload");
-
- exit(0);
- } ReapZombie(pid);
-
- exit(0);
- } ReapZombie(pid);
-
- sleep(15);
- system("/usr/bin/killall backboardd SpringBoard sbreload");
-}
-
- (void) close {
UpdateExternalStatus(0);
reload: {
UIProgressHUD *hud([delegate_ addProgressHUD]);
[hud setText:UCLocalize("LOADING")];
- [self performSelector:@selector(reloadSpringBoard) withObject:nil afterDelay:0.5];
+ [delegate_ performSelector:@selector(reloadSpringBoard) withObject:nil afterDelay:0.5];
return;
}
NSString *directory = [stack lastObject];
[stack addObject:[file stringByAppendingString:@"/"]];
[files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
- ([stack count] - 2) * 3, "",
+ int(([stack count] - 2) * 3), "",
[file substringFromIndex:[directory length]]
]];
}
_H<NSString> name_;
bool commercial_;
std::vector<std::pair<_H<NSString>, _H<NSString>>> buttons_;
+ _H<UIActionSheet> sheet_;
_H<UIBarButtonItem> button_;
+ _H<NSArray> versions_;
}
- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer;
return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", (id) name_]];
}
+- (void) _clickButtonWithPackage:(Package *)package {
+ [delegate_ installPackage:package];
+}
+
- (void) _clickButtonWithName:(NSString *)name {
if ([name isEqualToString:@"CLEAR"])
- [delegate_ clearPackage:package_];
- else if ([name isEqualToString:@"INSTALL"])
- [delegate_ installPackage:package_];
- else if ([name isEqualToString:@"REINSTALL"])
- [delegate_ installPackage:package_];
+ return [delegate_ clearPackage:package_];
else if ([name isEqualToString:@"REMOVE"])
- [delegate_ removePackage:package_];
- else if ([name isEqualToString:@"UPGRADE"])
- [delegate_ installPackage:package_];
+ return [delegate_ removePackage:package_];
+ else if ([name isEqualToString:@"DOWNGRADE"]) {
+ sheet_ = [[[UIActionSheet alloc]
+ initWithTitle:nil
+ delegate:self
+ cancelButtonTitle:nil
+ destructiveButtonTitle:nil
+ otherButtonTitles:nil
+ ] autorelease];
+
+ for (Package *version in (id) versions_)
+ [sheet_ addButtonWithTitle:[version latest]];
+ [sheet_ setContext:@"version"];
+
+ [delegate_ showActionSheet:sheet_ fromItem:[[self navigationItem] rightBarButtonItem]];
+ return;
+ }
+
+ else if ([name isEqualToString:@"INSTALL"]);
+ else if ([name isEqualToString:@"REINSTALL"]);
+ else if ([name isEqualToString:@"UPGRADE"]);
else _assert(false);
+
+ [delegate_ installPackage:package_];
}
- (void) actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)button {
NSString *context([sheet context]);
+ if (sheet_ == sheet)
+ sheet_ = nil;
if ([context isEqualToString:@"modify"]) {
if (button != [sheet cancelButtonIndex]) {
[self _clickButtonWithName:buttons_[button].first];
}
+ [sheet dismissWithClickedButtonIndex:button animated:YES];
+ } else if ([context isEqualToString:@"version"]) {
+ if (button != [sheet cancelButtonIndex]) {
+ Package *version([versions_ objectAtIndex:button]);
+ if (IsWildcat_)
+ [self performSelector:@selector(_clickButtonWithPackage:) withObject:version afterDelay:0];
+ else
+ [self _clickButtonWithPackage:version];
+ }
+
[sheet dismissWithClickedButtonIndex:button animated:YES];
}
}
for (const auto &button : buttons_)
[buttons addObject:button.second];
- UIActionSheet *sheet = [[[UIActionSheet alloc]
+ sheet_ = [[[UIActionSheet alloc]
initWithTitle:nil
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil
] autorelease];
- for (NSString *button in buttons) [sheet addButtonWithTitle:button];
- if (!IsWildcat_) {
- [sheet addButtonWithTitle:UCLocalize("CANCEL")];
- [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
- }
- [sheet setContext:@"modify"];
+ for (NSString *button in buttons)
+ [sheet_ addButtonWithTitle:button];
+ [sheet_ setContext:@"modify"];
- [delegate_ showActionSheet:sheet fromItem:[[self navigationItem] rightBarButtonItem]];
+ [delegate_ showActionSheet:sheet_ fromItem:[[self navigationItem] rightBarButtonItem]];
}
}
- (void) reloadData {
[super reloadData];
+ [sheet_ dismissWithClickedButtonIndex:[sheet_ cancelButtonIndex] animated:YES];
+ sheet_ = nil;
+
package_ = [database_ packageWithName:name_];
+ versions_ = [package_ downgrades];
buttons_.clear();
buttons_.push_back(std::make_pair(@"REINSTALL", UCLocalize("REINSTALL")));
if (![package_ uninstalled])
buttons_.push_back(std::make_pair(@"REMOVE", UCLocalize("REMOVE")));
+ if ([versions_ count] != 0)
+ buttons_.push_back(std::make_pair(@"DOWNGRADE", UCLocalize("DOWNGRADE")));
}
NSString *title;
_profile(PackageTable$reloadData$Filter)
for (Package *package in packages)
- if ([package valid] && filter(package))
+ if (filter(package))
[filtered addObject:package];
_end
[alert setCancelButtonIndex:0];
[alert setMessage:
- @"Copyright \u00a9 2008-2014\n"
+ @"Copyright \u00a9 2008-2015\n"
"SaurikIT, LLC\n"
"\n"
"Jay Freeman (saurik)\n"
Database *database([Database sharedInstance]);
- if ([command isEqualToString:@"package-icon"]) {
+ if (false);
+ else if ([command isEqualToString:@"application-icon"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+
+ UIImage *icon(nil);
+
+ if (icon == nil && $SBSCopyIconImagePNGDataForDisplayIdentifier != NULL) {
+ NSData *data([$SBSCopyIconImagePNGDataForDisplayIdentifier(path) autorelease]);
+ icon = [UIImage imageWithData:data];
+ }
+
+ if (icon == nil)
+ if (NSString *file = SBSCopyIconImagePathForDisplayIdentifier(path))
+ icon = [UIImage imageAtPath:file];
+
+ if (icon == nil)
+ icon = [UIImage imageNamed:@"unknown.png"];
+
+ [self _returnPNGWithImage:icon forRequest:request];
+ } else if ([command isEqualToString:@"package-icon"]) {
if (path == nil)
goto fail;
path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[section addToCount];
_profile(SectionsView$reloadData$Filter)
- if (![package valid] || ![package visible])
+ if (![package visible])
continue;
_end
const char *package([name_ UTF8String]);
bool on([ignoredSwitch_ isOn]);
- pid_t pid(ExecFork());
- if (pid == 0) {
- FILE *dpkg(popen("/usr/libexec/cydo --set-selections", "w"));
- fwrite(package, strlen(package), 1, dpkg);
-
- if (on)
- fwrite(" hold\n", 6, 1, dpkg);
- else
- fwrite(" install\n", 9, 1, dpkg);
+ FILE *dpkg(popen("/usr/libexec/cydia/cydo --set-selections", "w"));
+ fwrite(package, strlen(package), 1, dpkg);
- pclose(dpkg);
+ if (on)
+ fwrite(" hold\n", 6, 1, dpkg);
+ else
+ fwrite(" install\n", 9, 1, dpkg);
- exit(0);
- } ReapZombie(pid);
+ pclose(dpkg);
}
- (void) onIgnored:(id)control {
[Sources_ removeObjectForKey:[source key]];
- [delegate_ _saveConfig];
- [delegate_ reloadDataWithInvocation:nil];
+ [delegate_ syncData];
}
}
switch (button) {
case 1: {
NSString *href = [[alert textField] text];
-
- static RegEx href_r("(http(s?)://|file:///)[^# ]*");
- if (!href_r(href)) {
- UIAlertView *alert = [[[UIAlertView alloc]
- initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("INVALID_URL")]
- message:UCLocalize("INVALID_URL_EX")
- delegate:self
- cancelButtonTitle:UCLocalize("OK")
- otherButtonTitles:nil
- ] autorelease];
-
- [alert setContext:@"badurl"];
- [alert show];
-
+ href = VerifySource(href);
+ if (href == nil)
break;
- }
-
- if (![href hasSuffix:@"/"])
- href_ = [href stringByAppendingString:@"/"];
- else
- href_ = href;
+ href_ = href;
trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
[self _loaded];
}
+- (void) reloadSpringBoard {
+ if (kCFCoreFoundationVersionNumber >= 700) // XXX: iOS 6.x
+ system("/usr/libexec/cydia/cydo /bin/launchctl stop com.apple.backboardd");
+ else
+ system("/usr/libexec/cydia/cydo /bin/launchctl stop com.apple.SpringBoard");
+ sleep(15);
+ system("/usr/bin/killall backboardd SpringBoard");
+}
+
- (void) _saveConfig {
SaveConfig(database_);
}
CydiaAddSource(href, distribution, sections);
}
-- (void) addTrivialSource:(NSString *)href {
+// XXX: this method should not return anything
+- (BOOL) addTrivialSource:(NSString *)href {
CydiaAddSource(href, @"./");
+ return YES;
}
- (void) resolve {
- (void) _uicache {
_trace();
-
- if (UpgradeCydia_ && Finish_ > 0) {
- setreugid(0, 0);
- system("su -c /usr/bin/uicache mobile");
- } else {
- system("/usr/bin/uicache");
- }
-
+ system("/usr/bin/uicache");
_trace();
}
@synchronized (self) {
for (Package *broken in (id) broken_) {
[broken remove];
- NSString *id = [broken id];
-
+ NSString *id(ShellEscape([broken id]));
system([[NSString stringWithFormat:@"/usr/libexec/cydia/cydo /bin/rm -f"
" /var/lib/dpkg/info/%@.prerm"
" /var/lib/dpkg/info/%@.postrm"
" /var/lib/dpkg/info/%@.preinst"
" /var/lib/dpkg/info/%@.postinst"
" /var/lib/dpkg/info/%@.extrainst_"
- , id, id, id, id, id] UTF8String]);
+ "", id, id, id, id, id] UTF8String]);
}
[self resolve];
controller = [[[SectionController alloc] initWithDatabase:database_ source:nil section:argument] autorelease];
}
- if (!external && [base isEqualToString:@"sources"]) {
+ if ([base isEqualToString:@"sources"]) {
if ([argument isEqualToString:@"add"]) {
controller = [[[SourcesController alloc] initWithDatabase:database_] autorelease];
[(SourcesController *)controller showAddSourcePrompt];
UpdateExternalStatus(0);
[self removeStashController];
-
- pid_t pid(ExecFork());
- if (pid == 0) {
- execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
- perror("launchctl stop");
-
- exit(0);
- } ReapZombie(pid);
+ [self reloadSpringBoard];
}
- (void) setupViewControllers {
[window_ makeKey:self];
[window_ setHidden:NO];
+ if (access("/.cydia_no_stash", F_OK) == 0);
+ else {
+
if (false) stash: {
[self addStashController];
// XXX: this would be much cleaner as a yieldToSelector:
Stash_("/usr/share");
//Stash_("/var/lib");
+ }
+
database_ = [Database sharedInstance];
[database_ setDelegate:self];
}
- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {
+ if (!IsWildcat_) {
+ [sheet addButtonWithTitle:UCLocalize("CANCEL")];
+ [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
+ }
+
if (item != nil && IsWildcat_) {
[sheet showFromBarButtonItem:item animated:YES];
} else {
return _NSUserDefaults$objectForKey$(self, _cmd, key);
}
+static NSMutableDictionary *AutoreleaseDeepMutableCopyOfDictionary(CFTypeRef type) {
+ if (type == NULL)
+ return nil;
+ if (CFGetTypeID(type) != CFDictionaryGetTypeID())
+ return nil;
+ CFTypeRef copy(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, type, kCFPropertyListMutableContainers));
+ CFRelease(type);
+ return [(NSMutableDictionary *) copy autorelease];
+}
+
+int main_store(int, char *argv[]);
+
int main(int argc, char *argv[]) {
- setreugid(501, 501);
+#ifdef __arm64__
+ const char *argv0(argv[0]);
+ if (const char *slash = strrchr(argv0, '/'))
+ argv0 = slash + 1;
+ if (false);
+ else if (!strcmp(argv0, "store"))
+ return main_store(argc, argv);
+#endif
+
+ int fd(open("/tmp/cydia.log", O_WRONLY | O_APPEND | O_CREAT, 0644));
+ dup2(fd, 2);
+ close(fd);
NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
Locale_ = CFLocaleCopyCurrent();
Languages_ = [NSLocale preferredLanguages];
- //CFStringRef locale(CFLocaleGetIdentifier(Locale_));
- //NSLog(@"%@", [Languages_ description]);
+ std::string languages;
+ const char *translation(NULL);
- const char *lang;
+ // XXX: this isn't really a language, but this is compatible with older Cydia builds
if (Locale_ != NULL)
- lang = [(NSString *) CFLocaleGetIdentifier(Locale_) UTF8String];
- else if (Languages_ != nil && [Languages_ count] != 0)
- lang = [[Languages_ objectAtIndex:0] UTF8String];
- else
- // XXX: consider just setting to C and then falling through?
- lang = NULL;
-
- if (lang != NULL) {
- RegEx pattern("([a-z][a-z])(?:-[A-Za-z]*)?(_[A-Z][A-Z])?");
- lang = !pattern(lang) ? NULL : [pattern->*@"%1$@%2$@" UTF8String];
- }
+ if (const char *language = [(NSString *) CFLocaleGetIdentifier(Locale_) UTF8String]) {
+ RegEx pattern("([a-z][a-z])(?:-[A-Za-z]*)?(_[A-Z][A-Z])?");
+ if (pattern(language)) {
+ translation = strdup([pattern->*@"%1$@%2$@" UTF8String]);
+ languages += translation;
+ languages += ",";
+ }
+ }
- NSLog(@"Setting Language: %s", lang);
+ if (Languages_ != nil)
+ for (NSString *language : Languages_) {
+ languages += [language UTF8String];
+ languages += ",";
+ }
- if (lang != NULL) {
- setenv("LANG", lang, true);
- std::setlocale(LC_ALL, lang);
- }
+ languages += "en";
+ NSLog(@"Setting Language: [%s] %s", translation, languages.c_str());
/* }}} */
/* Index Collation {{{ */
if (Class $UILocalizedIndexedCollation = objc_getClass("UILocalizedIndexedCollation")) { @try {
Advanced_ = YES;
Cache_ = [[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia", @"/var/mobile"] retain];
+ mkdir([Cache_ UTF8String], 0755);
/*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
alloc_ = alloc->method_imp;
MetaFile_.Open("/var/mobile/Library/Cydia/metadata.cb0");
_trace();
- // XXX: port this to NSUserDefaults when you aren't in such a rush
- Values_ = [[[(NSDictionary *) CFPreferencesCopyAppValue(CFSTR("CydiaValues"), CFSTR("com.saurik.Cydia")) autorelease] mutableCopy] autorelease];
- Sections_ = [[[(NSDictionary *) CFPreferencesCopyAppValue(CFSTR("CydiaSections"), CFSTR("com.saurik.Cydia")) autorelease] mutableCopy] autorelease];
- Sources_ = [[[(NSDictionary *) CFPreferencesCopyAppValue(CFSTR("CydiaSources"), CFSTR("com.saurik.Cydia")) autorelease] mutableCopy] autorelease];
+ Values_ = AutoreleaseDeepMutableCopyOfDictionary(CFPreferencesCopyAppValue(CFSTR("CydiaValues"), CFSTR("com.saurik.Cydia")));
+ Sections_ = AutoreleaseDeepMutableCopyOfDictionary(CFPreferencesCopyAppValue(CFSTR("CydiaSections"), CFSTR("com.saurik.Cydia")));
+ Sources_ = AutoreleaseDeepMutableCopyOfDictionary(CFPreferencesCopyAppValue(CFSTR("CydiaSources"), CFSTR("com.saurik.Cydia")));
Version_ = [(NSNumber *) CFPreferencesCopyAppValue(CFSTR("CydiaVersion"), CFSTR("com.saurik.Cydia")) autorelease];
_trace();
_H<NSMutableArray> broken([NSMutableArray array]);
for (NSString *key in (id) Sources_)
- if ([key rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"# "]].location != NSNotFound)
+ if ([key rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"# "]].location != NSNotFound || ![([[Sources_ objectForKey:key] objectForKey:@"URI"] ?: @"/") hasSuffix:@"/"])
[broken addObject:key];
if ([broken count] != 0)
for (NSString *key in (id) broken)
Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
-#define MobileSubstrate_(name) \
- if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", F_OK) == 0) { \
- void *handle(dlopen("/Library/MobileSubstrate/DynamicLibraries/" #name ".dylib", RTLD_LAZY | RTLD_GLOBAL)); \
- if (handle == NULL) \
- NSLog(@"%s", dlerror()); \
- }
-
- MobileSubstrate_(Activator)
- MobileSubstrate_(libstatusbar)
- MobileSubstrate_(SimulatedKeyEvents)
- MobileSubstrate_(WinterBoard)
-
- /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
- dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
-
if (kCFCoreFoundationVersionNumber > 1000)
system("/usr/libexec/cydia/cydo /usr/libexec/cydia/setnsfpn /var/lib");
_assert(errno == ENOENT);
}
+ system("/usr/libexec/cydia/cydo /bin/ln -sf /var/mobile/Library/Caches/com.saurik.Cydia/sources.list /etc/apt/sources.list.d/cydia.list");
+
/* APT Initialization {{{ */
_assert(pkgInitConfig(*_config));
_assert(pkgInitSystem(*_config, _system));
- if (lang != NULL)
- _config->Set("APT::Acquire::Translation", lang);
+ _config->Set("Acquire::AllowInsecureRepositories", true);
+ _config->Set("Acquire::Check-Valid-Until", false);
+ _config->Set("Dir::Bin::Methods::store", "/Applications/Cydia.app/store");
+
+ _config->Set("pkgCacheGen::ForceEssential", "");
+
+ if (translation != NULL)
+ _config->Set("APT::Acquire::Translation", translation);
+ _config->Set("Acquire::Languages", languages);
// XXX: this timeout might be important :(
//_config->Set("Acquire::http::Timeout", 15);
_config->Set("Acquire::http::MaxParallel", usermem >= 384 * 1024 * 1024 ? 16 : 3);
- mkdir([Cache_ UTF8String], 0755);
mkdir([Cache("archives") UTF8String], 0755);
mkdir([Cache("archives/partial") UTF8String], 0755);
_config->Set("Dir::Cache", [Cache_ UTF8String]);
+ symlink("/var/lib/apt/extended_states", [Cache("extended_states") UTF8String]);
+ _config->Set("Dir::State", [Cache_ UTF8String]);
+
mkdir([Cache("lists") UTF8String], 0755);
mkdir([Cache("lists/partial") UTF8String], 0755);
mkdir([Cache("periodic") UTF8String], 0755);
std::string logs("/var/mobile/Library/Logs/Cydia");
mkdir(logs.c_str(), 0755);
- _config->Set("Dir::Log::Terminal", logs + "/apt.log");
+ _config->Set("Dir::Log", logs);
_config->Set("Dir::Bin::dpkg", "/usr/libexec/cydia/cydo");
/* }}} */
/* }}} */
$SBSSetInterceptsMenuButtonForever = reinterpret_cast<void (*)(bool)>(dlsym(RTLD_DEFAULT, "SBSSetInterceptsMenuButtonForever"));
+ $SBSCopyIconImagePNGDataForDisplayIdentifier = reinterpret_cast<NSData *(*)(NSString *)>(dlsym(RTLD_DEFAULT, "SBSCopyIconImagePNGDataForDisplayIdentifier"));
const char *symbol(kCFCoreFoundationVersionNumber >= 800 ? "MGGetBoolAnswer" : "GSSystemHasCapability");
BOOL (*GSSystemHasCapability)(CFStringRef) = reinterpret_cast<BOOL (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, symbol));