commit 954e43aa2fb69feb961fda2cce834129e371f491 Author: Victor Bodinaud Date: Mon Mar 3 13:59:05 2025 +0100 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..4d0cbd0 Binary files /dev/null and b/.DS_Store differ diff --git a/Cydia.app/Cydia b/Cydia.app/Cydia new file mode 100755 index 0000000..3c72315 --- /dev/null +++ b/Cydia.app/Cydia @@ -0,0 +1,6 @@ +#!/bin/bash +C=/${0} +C=${C%/*} +declare -a flags +[[ :${DYLD_INSERT_LIBRARIES}: == */MobileSubstrate.dylib: ]] && flags[${#flags[@]}]=--substrate +exec "${C:-.}"/Cydia_ "${flags[@]}" -- "$@" 2>>/tmp/cydia.log diff --git a/Cydia.app/Default-Portrait.png b/Cydia.app/Default-Portrait.png new file mode 100644 index 0000000..c24b27e Binary files /dev/null and b/Cydia.app/Default-Portrait.png differ diff --git a/Cydia.app/Default.png b/Cydia.app/Default.png new file mode 100644 index 0000000..ca46fda Binary files /dev/null and b/Cydia.app/Default.png differ diff --git a/Cydia.app/English.lproj/Localizable.strings b/Cydia.app/English.lproj/Localizable.strings new file mode 100644 index 0000000..d655c1e --- /dev/null +++ b/Cydia.app/English.lproj/Localizable.strings @@ -0,0 +1,210 @@ +"ABOUT" = "About"; +"ABOUT_CYDIA" = "About Cydia Installer"; +"ACCEPT_NEW_COPY" = "Accept The New Copy"; +"ADD" = "Add"; +"ADD_ANYWAY" = "Add Anyway"; +"ADD_SOURCE" = "Add Source"; +"ADMINISTRATIVE_INFORMATION" = "Administrative Information"; +"ADVANCED_SEARCH" = "Advanced Search"; +"ALL_PACKAGES" = "All Packages"; +"APPLE" = "Apple"; +"AUTHOR" = "Author"; +"AVAILABLE_UPGRADES" = "Available Upgrades"; +"BROWSER" = "Browser"; +"BY" = "by %@"; +"CANCEL" = "Cancel"; +"CANCEL_CLEAR" = "Cancel and Clear"; +"CANCEL_OPERATION" = "Cancel Operation"; +"CANNOT_COMPLY" = "Cannot Comply"; +"CANNOT_COMPLY_EX" = "The requested modifications cannot be applied due to required dependencies or conflicts that cannot be automatically found or fixed."; +"CANNOT_LOCATE_PACKAGE" = "Cannot Locate Package"; +"CHANGE_PACKAGE_SETTINGS" = "Change Package Settings"; +"CHANGES" = "Changes"; +"CLEAN_ARCHIVES" = "Clean Archives"; +"CLEAR" = "Clear"; +"CLOSE" = "Close"; +"CLOSE_CYDIA" = "Close Cydia (Restart)"; +"COLON_DELIMITED" = "%@: %@"; +"COMMA_DELIMITED" = "%@, %@"; +"COMING_SOON" = "Coming Soon!"; +"COMPLETE" = "Complete"; +"COMPLETE_UPGRADE" = "Complete Upgrade"; +"CONFIGURATION_UPGRADE" = "Configuration Upgrade"; +"CONFIGURATION_UPGRADE_EX" = "The following file has been changed by both the package maintainer and by you (or for you by a script)."; +"COMMERCIAL_APPLICATIONS" = "Commercial Applications"; +"CONFIRM" = "Confirm"; +"CONSOLE_PACKAGE" = "This is a console package!"; +"CONSOLE_UTILITIES_DAEMONS" = "Console Utilities & Daemons"; +"CONTINUE_QUEUING" = "Continue Queuing"; +"COUNTS_NONZERO_EX" = "So, I just counted some important-looking values, and they didn't sum to zero. This is scary. I don't know why I don't like this anymore, but I think APT was adamant this should not occur."; +"CREDITS" = "Credits"; +"CYDIA_STORE" = "Cydia Store"; +"DATABASE" = "Database"; +"DETAILS" = "Details"; +"DEVELOPER" = "Developer"; +"DEVELOPER_EX" = "No Filters"; +"DEVELOPERS_ONLY" = "Developers Only"; +"DISK_FREEING" = "Disk Freeing"; +"DISK_USING" = "Disk Using"; +"DONATE_USING" = "Donate using %@"; +"DONE" = "Done"; +"DOUBLE_QUOTED" = "“%@”"; +"DOWNGRADE" = "Downgrade"; +"DOWNLOADING" = "Downloading"; +"DOWNLOADING_" = "Downloading %@"; +"EDIT" = "Edit"; +"ENTER_APT_URL" = "Enter Cydia/APT URL"; +"ENTERED_BY_USER" = "Entered by User"; +"ERROR" = "Error"; +"ESSENTIAL_UPGRADE" = "Essential Upgrade"; +"ESSENTIAL_UPGRADES" = "%d Essential Upgrades"; +"ESSENTIAL_UPGRADE_EX" = "One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."; +"EXCLAMATION" = "%@!"; +"EXPANDED_SIZE" = "Expanded Size"; +"EXPERT" = "Expert"; +"FEATURED_PACKAGES" = "Featured Packages"; +"FAQ" = "FAQ"; +"FILES" = "Files"; +"FILES_INSTALLED_TO" = "files installed to %@"; +"FILESYSTEM_CONTENT" = "Filesystem Content"; +"FOLLOW_ON_TWITTER" = "Follow %@ on Twitter"; +"FORCE_REMOVAL" = "Force Removal"; +"FORCIBLY_CLEAR" = "Forcibly Clear"; +"FREE_APPLICATIONS" = "Free Applications"; +"FREE_EXTENSIONS" = "Free Extensions"; +"FREQUENTLY_ASKED_QUESTIONS" = "Frequently Asked Questions"; +"FROM" = "from %@"; +"FROWNY_PANTS" = "Frowny Pants"; +"FUTURE_FEATURE_ROADMAP" = "Future Feature Roadmap"; +"HACKER" = "Hacker"; +"HACKER_EX" = "+ Command Line"; +"HALFINSTALLED_PACKAGE" = "Half-Installed Package"; +"HALFINSTALLED_PACKAGES" = "%d Half-Installed Packages"; +"HALFINSTALLED_PACKAGE_EX" = "When the shell scripts associated with packages fail, they are left in a bad state known as either half-configured or half-installed. These errors don't go away and instead continue to cause issues. These scripts can be deleted and the packages forcibly removed."; +"HOME" = "Home"; +"ID" = "ID"; +"IGNORE_UPGRADES" = "Ignore Upgrades"; +"IGNORED_UPGRADES" = "Ignored Upgrades"; +"ILLEGAL_PACKAGE_IDENTIFIER" = "Illegal Package Identifier"; +"INSTALL" = "Install"; +"INSTALLED" = "Installed"; +"INSTALLED_BY_PACKAGE" = "Installed by Package"; +"INSTALLED_DETAILS" = "Installed Package"; +"INSTALLED_FILES" = "Installed Files"; +"KEEP_OLD_COPY" = "Keep My Old Copy"; +"LOADING" = "Loading"; +"LOADING_DATA" = "Loading Data"; +"LOCAL" = "Local"; +"LOGIN" = "Login"; +"MAILING_LISTS" = "Mailing Lists"; +"MAINTAINER" = "Contact"; +"MANAGE" = "Manage"; +"MODIFICATIONS" = "Modifications"; +"MODIFY" = "Modify"; +"MORE_INFORMATION" = "More Information"; +"MORE_PACKAGE_SOURCES" = "More Package Sources"; +"NAME" = "Name"; +"NETWORK_ERROR" = "Network Error"; +"NEVER_OR_UNKNOWN" = "Never or Unknown"; +"NEW" = "New"; +"NEW_AT" = "New at %@"; +"NEW_INSTALL" = "New Install"; +"NO" = "No"; +"NO_SECTION" = "(No Section)"; +"NOT_RATED" = "Not Rated"; +"NOTE" = "Note"; +"NOT_REPOSITORY" = "Did not Find Repository"; +"NOT_REPOSITORY_EX" = "The indicated repository could not be found. This could be because you are trying to add a legacy Installer repository (these are not supported). Also, this interface is only capable of working with exact repository URLs. If you host a repository and are having issues please contact the author of Cydia with any questions you have."; +"OK" = "OK"; +"OKAY" = "Okay"; +"PACKAGE_CANNOT_BE_FOUND" = "The package %@ cannot be found in your current sources. I might recommend installing more sources."; +"PACKAGE_CANNOT_BE_FOUND_EX" = "This issue may also be caused by a number of other factors. The most common one is that your package catalog is out of date. To manually refresh, click the Refresh button on the Changes tab. This package may simply no longer exist. It is also possible you are using an old firmware version: users should attempt to remain reasonably up to date with Apple's firmware releases."; +"PACKAGE_DAMAGED" = "Package Damaged"; +"PACKAGE_DETAILS" = "Package Details"; +"PACKAGE_QUEUE" = "Package Queue"; +"PACKAGES" = "Packages"; +"PACKAGES_EX" = "View or remove packages you previously installed."; +"PARENTHETICAL" = "%@ (%@)"; +"PASSWORD" = "password"; +"PERFORM_SELECTIONS" = "Perform Selections"; +"PREPARE_ARCHIVES" = "Prepare Archives"; +"PRODUCTS" = "Products"; +"PURGE" = "Purge"; +"Q_D" = "Q'd"; +"QUEUED_FOR" = "Queued for %@"; +"QUESTION" = "%@?"; +"REBOOT_DEVICE" = "Reboot Device"; +"RECENT_CHANGES_UPDATES" = "Recent Changes/Updates"; +"RECOMMENDED_BOOKS" = "Recommended Books"; +"REFRESH" = "Refresh"; +"REFRESHING_DATA" = "Refreshing Data"; +"REINSTALL" = "Reinstall"; +"RELOAD" = "Reload"; +"RELOAD_SPRINGBOARD" = "Reload SpringBoard"; +"RELOADING_DATA" = "Reloading Data"; +"REMOVE" = "Remove"; +"REMOVING_ESSENTIALS" = "Removing Essentials"; +"REMOVING_ESSENTIALS_EX" = "This operation involves the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. If you continue, you may not be able to use Cydia to repair any damage."; +"REPAIRING" = "Repairing"; +"REPOSITORY_SIGNATURE" = "View Repository Signature"; +"REQUEST_ADVERTISING_SPACE" = "Request Advertising Space"; +"REQUIREMENTS_FOR_LISTING" = "Requirements for Listing"; +"RESTART_SPRINGBOARD" = "Restart SpringBoard"; +"RESUBMIT_FORM" = "Are you sure you want to submit this form again?"; +"RESUMING_AT" = "Resuming At"; +"RETURN_TO_CYDIA" = "Return to Cydia"; +"ROLE_EX" = "Not all of the packages available via Cydia are designed to be used by all users. Please categorize yourself so that Cydia can apply helpful filters.\n\nThis choice can be changed from \"Settings\" under the \"Manage\" tab."; +"RUNNING" = "Running"; +"SAFE" = "Safe"; +"SAMPLE" = "Sample"; +"SCREENSHOT" = "Screenshot"; +"SCREENSHOTS" = "Screenshots"; +"SEARCH" = "Search"; +"SEARCH_EX" = "Package Names & Descriptions"; +"SECTION" = "Section"; +"SECTION_VISIBILITY" = "Section Visibility"; +"SECTIONS" = "Sections"; +"SEE_WHAT_CHANGED" = "See What Changed"; +"SETTINGS" = "Settings"; +"SHOW_ALL_CHANGES" = "Show All Changes"; +"SHOW_ALL_CHANGES_EX" = "Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."; +"SIGNED_PACKAGE" = "This package has been signed."; +"SIMPLE" = "Simple"; +"SINGLE_QUOTED" = "‘%@’"; +"SLASH_DELIMITED" = "%@ / %@"; +"SOURCE_DETAILS" = "Source Information"; +"SOURCE_WARNING" = "Source Warning"; +"SOURCES" = "Sources"; +"SOURCES_EX" = "List current sources and add custom ones you may know."; +"SPONSOR" = "Sponsor"; +"SPONSORING_PRODUCTS" = "Sponsoring Products"; +"STATISTICS" = "Statistics"; +"STILL_BROKEN_EX" = "Trying to fix the broken selections did not seem to help."; +"STORAGE" = "Storage"; +"STORAGE_EX" = "See how much room you have left for new packages."; +"SUBMIT" = "Submit"; +"SUPPORT_KNOWN_ISSUES" = "Support & Known Issues"; +"TEMPORARY_IGNORE" = "Ignore (Temporary)"; +"THANK_YOU" = "Thank You"; +"QUEUE" = "Queue"; +"UNABLE_TO_COMPLY" = "Unable to Comply"; +"UNABLE_TO_COMPLY_EX" = "This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. In order to continue and force this operation you will need to be activate the Advanced mode under to continue and force this operation you will need to be activate the Advanced mode under Settings."; +"UNABLE_TO_LOAD" = "Unable to Load"; +"UNKNOWN" = "Unknown"; +"UNSAFE" = "Unsafe"; +"UPDATING_DATABASE" = "Updating Database"; +"UPDATING_SOURCES" = "Updating Sources"; +"UPGRADE" = "Upgrade"; +"UPGRADE_ESSENTIAL" = "Upgrade Essential"; +"UPGRADING_TO_READ_THIS" = "Upgrading to %@? Read This"; +"USER" = "User"; +"USER_EX" = "Graphical Only"; +"USER_GUIDES" = "User Guides"; +"USERNAME" = "username"; +"VERIFICATION_ERROR" = "Verification Error"; +"VERIFYING_URL" = "Verifying URL"; +"VERSION" = "Version"; +"WARNING" = "Warning"; +"WELCOME_TO_CYDIA" = "Welcome to Cydia™"; +"WHO_ARE_YOU" = "Who Are You?"; +"YES" = "Yes"; diff --git a/Cydia.app/English.lproj/Sections_.strings b/Cydia.app/English.lproj/Sections_.strings new file mode 100644 index 0000000..9cc7ba1 --- /dev/null +++ b/Cydia.app/English.lproj/Sections_.strings @@ -0,0 +1,45 @@ +"Administration" = ""; +"App Addons" = ""; +"Archiving" = ""; +"Battery" = ""; +"Carrier Bundles" = ""; +"Carriers" = ""; +"Complete" = ""; +"Data Storage" = ""; +"Development" = ""; +"Dialer" = ""; +"Dictionaries" = ""; +"Dock" = ""; +"eBooks" = ""; +"Education" = ""; +"Emulation" = ""; +"Entertainment" = ""; +"Games" = ""; +"Icy" = ""; +"Java" = ""; +"Keyboards" = ""; +"Localization" = ""; +"LockScreen" = ""; +"Messaging" = ""; +"Multimedia" = ""; +"Networking" = ""; +"Notifications" = ""; +"Packaging" = ""; +"Productivity" = ""; +"Repositories" = ""; +"Ringtones" = ""; +"Scripting" = ""; +"Security" = ""; +"Sliders" = ""; +"SMS" = ""; +"System" = ""; +"Terminal Support" = ""; +"Text Editors" = ""; +"Themes" = ""; +"Toys" = ""; +"Tweaks" = ""; +"Utilities" = ""; +"Wallpaper" = ""; +"Weather" = ""; +"WebClips" = ""; +"Widgets" = ""; diff --git a/Cydia.app/French.lproj/Localizable.strings b/Cydia.app/French.lproj/Localizable.strings new file mode 100644 index 0000000..bdd6988 --- /dev/null +++ b/Cydia.app/French.lproj/Localizable.strings @@ -0,0 +1,210 @@ +"ABOUT" = "À propos"; +"ABOUT_CYDIA" = "À propos de Cydia"; +"ACCEPT_NEW_COPY" = "Accepter la nouvelle version"; +"ADD" = "Ajouter"; +"ADD_ANYWAY" = "Ajouter quand même"; +"ADD_SOURCE" = "Ajouter la source"; +"ADMINISTRATIVE_INFORMATION" = "Information administrative"; +"ADVANCED_SEARCH" = "Recherche avancée"; +"ALL_PACKAGES" = "Tous les paquets"; +"APPLE" = "Apple"; +"AUTHOR" = "Auteur"; +"AVAILABLE_UPGRADES" = "Mises à jour disponibles"; +"BROWSER" = "Navigateur"; +"BY" = "par %@"; +"CANCEL" = "Annuler"; +"CANCEL_CLEAR" = "Annuler et vider la file"; +"CANCEL_OPERATION" = "Annuler l'opération"; +"CANNOT_COMPLY" = "Impossible"; +"CANNOT_COMPLY_EX" = "Les modifications demandées ne peuvent pas être appliquées à cause de dépendances ou de conflits impossibles à identifier automatiquement."; +"CANNOT_LOCATE_PACKAGE" = "Impossible de trouver le paquet"; +"CHANGE_PACKAGE_SETTINGS" = "Changer les réglages du paquet"; +"CHANGES" = "Nouveautés"; +"CLEAN_ARCHIVES" = "Nettoyer les archives"; +"CLEAR" = "Nettoyer"; +"CLOSE" = "Fermer"; +"CLOSE_CYDIA" = "Quitter Cydia (Redémarrer)"; +"COLON_DELIMITED" = "%@ : %@"; +"COMMA_DELIMITED" = "%@, %@"; +"COMING_SOON" = "À venir!"; +"COMPLETE" = "Effectué"; +"COMPLETE_UPGRADE" = "Mise à Jour effectuée"; +"CONFIGURATION_UPGRADE" = "Configuration de la mise à jour"; +"CONFIGURATION_UPGRADE_EX" = "Le(s) fichier(s) suivant(s) ont été modifiés par le responsable de ce paquet et par vous (ou pour vous par un script)."; +"COMMERCIAL_APPLICATIONS" = "Applications commerciales"; +"CONFIRM" = "Confirmer"; +"CONSOLE_PACKAGE" = "Ceci est un paquet console!"; +"CONSOLE_UTILITIES_DAEMONS" = "Utilitaires de terminal & daemons"; +"CONTINUE_QUEUING" = "Mettre dans la file d'attente"; +"COUNTS_NONZERO_EX" = "Après avoir calculé la somme de valeurs importantes, elle n'est pas égale à zéro. Ceci est épeurant. J'ai aucune idée pourquoi ceci est dangereux, mais je crois que APT ne voulait absolument pas que ceci arrive."; +"CREDITS" = "Crédits"; +"CYDIA_STORE" = "Cydia Store"; +"DATABASE" = "Base de données"; +"DETAILS" = "Détails"; +"DEVELOPER" = "Développeur"; +"DEVELOPER_EX" = "Aucun filtre"; +"DEVELOPERS_ONLY" = "Développeurs seulement"; +"DISK_FREEING" = "Espace disque libéré"; +"DISK_USING" = "Espace disque utilisé"; +"DONATE_USING" = "Donnez en utilisant %@"; +"DONE" = "OK"; +"DOUBLE_QUOTED" = "« $@ »"; +"DOWNGRADE" = "Rétrograder"; +"DOWNLOADING" = "Télécharger"; +"DOWNLOADING_" = "Téléchargement de %@ en cours"; +"EDIT" = "Modifier"; +"ENTER_APT_URL" = "Entrez une adresse Cydia/APT"; +"ENTERED_BY_USER" = "Entré par l'utilisateur"; +"ERROR" = "Erreur"; +"ESSENTIAL_UPGRADE" = "Mise à jour essentielle"; +"ESSENTIAL_UPGRADES" = "%d mises à jour essentielles"; +"ESSENTIAL_UPGRADE_EX" = "Un ou plusieurs paquets essentiels sont périmés. Si vous ne les mettez pas à jour, vous pourriez rencontrer des erreurs."; +"EXCLAMATION" = "%@ !"; +"EXPANDED_SIZE" = "Taille décompressée"; +"EXPERT" = "Expert"; +"FEATURED_PACKAGES" = "Paquets conseillés"; +"FAQ" = "FAQ"; +"FILES" = "Fichiers"; +"FILES_INSTALLED_TO" = "fichiers installés dans %@"; +"FILESYSTEM_CONTENT" = "Contenu"; +"FOLLOW_ON_TWITTER" = "Suivez %@ sur Twitter"; +"FORCE_REMOVAL" = "Forcer la suppression"; +"FORCIBLY_CLEAR" = "Forcer le nettoyage"; +"FREE_APPLICATIONS" = "Applications gratuites"; +"FREE_EXTENSIONS" = "Extensions gratuites"; +"FREQUENTLY_ASKED_QUESTIONS" = "Foire aux questions"; +"FROM" = "de %@"; +"FROWNY_PANTS" = "Pantalon fronçant"; +"FUTURE_FEATURE_ROADMAP" = "Feuille de route des mises à jour"; +"HACKER" = "Hacker"; +"HACKER_EX" = "+ CLI"; +"HALFINSTALLED_PACKAGE" = "Paquet à moitié installé"; +"HALFINSTALLED_PACKAGES" = "%d paquets à moitié installés"; +"HALFINSTALLED_PACKAGE_EX" = "Quand les scripts du paquet ne fonctionnent pas, ils laissent le paquet à moitié installé et à moitié configuré. Ces erreurs ne disparaissent pas et vont continuer à causer des problèmes. Ces scripts peuvent être supprimés et les paquets frocément désinstallés."; +"HOME" = "Accueil"; +"ID" = "ID"; +"IGNORE_UPGRADES" = "Ignorer les mises à jour"; +"IGNORED_UPGRADES" = "Mises à jour ignorées"; +"ILLEGAL_PACKAGE_IDENTIFIER" = "Identifiant du paquet illégal"; +"INSTALL" = "Installer"; +"INSTALLED" = "Installé"; +"INSTALLED_BY_PACKAGE" = "Installer par paquet"; +"INSTALLED_DETAILS" = "Paquet installé"; +"INSTALLED_FILES" = "Fichiers installés"; +"KEEP_OLD_COPY" = "Garder ma vieille copie"; +"LOADING" = "Chargement"; +"LOADING_DATA" = "Chargement des données"; +"LOCAL" = "Local"; +"LOGIN" = "S'identifier"; +"MAILING_LISTS" = "Listes de diffusion"; +"MAINTAINER" = "Contacter"; +"MANAGE" = "Gérer"; +"MODIFY" = "Modifier"; +"MODIFICATIONS" = "Modifications"; +"MORE_INFORMATION" = "Plus d'information"; +"MORE_PACKAGE_SOURCES" = "Plus de sources de paquets"; +"NAME" = "Nom"; +"NETWORK_ERROR" = "Erreur réseau"; +"NEVER_OR_UNKNOWN" = "Jamais ou inconnnu"; +"NEW" = "Nouveau"; +"NEW_AT" = "Nouveau à %@"; +"NEW_INSTALL" = "Nouvelle installation"; +"NO" = "Non"; +"NO_SECTION" = "(Pas de catégorie)"; +"NOT_RATED" = "Pas de cote"; +"NOTE" = "Note"; +"NOT_REPOSITORY" = "Impossible de trouver cette source"; +"NOT_REPOSITORY_EX" = "La source de paquets ne peut pas être trouvée. Ce pourrait être car c'est une source d'Installer (qui ne fonctionne pas avec Cydia). Cydia ne peut fonctionner qu'avec des adresses rigoureusement exactes. Si vous hébergez une source de paquets, contactez l'auteur de Cydia à propos de ces erreurs."; +"OK" = "OK"; +"OKAY" = "OK"; +"PACKAGE_CANNOT_BE_FOUND" = "Le paquet %@ est introuvable dans vos sources actuelles. Je vous recommande d'ajouter de nouvelles sources."; +"PACKAGE_CANNOT_BE_FOUND_EX" = "Ce problème peut aussi être causé par plusieurs autres facteurs. Le plus commun est que votre catalogue de paquets est périmé. Pour rafraîchir manuellement, utilisez le bouton Rafraîchir dans l'onglet Nouveautés. Le paquet pour simplement ne plus exister. Il est aussi posible que vous utilisez un vieux firmware; les utilisateurs devraient essayer de rester raisonnablement à date avec les sorties de firmware de Apple."; +"PACKAGE_DAMAGED" = "Paquet abimé"; +"PACKAGE_DETAILS" = "Détails du paquet"; +"PACKAGE_QUEUE" = "File d'attente de paquets"; +"PACKAGES" = "Paquets"; +"PACKAGES_EX" = "Voir ou supprimer des paquets précédemment installés."; +"PARENTHETICAL" = "%@ (%@)"; +"PASSWORD" = "mot de passe"; +"PERFORM_SELECTIONS" = "Performer des sélections"; +"PREPARE_ARCHIVES" = "Préparer les archives"; +"PRODUCTS" = "Produits"; +"PURGE" = "Purger"; +"Q_D" = "File"; +"QUEUED_FOR" = "File d'attente pour %@"; +"QUESTION" = "%@ ?"; +"REBOOT_DEVICE" = "Redémarrer l'appareil"; +"RECENT_CHANGES_UPDATES" = "Mises à jour ou changements récents"; +"RECOMMENDED_BOOKS" = "Livres recommandés"; +"REFRESH" = "Rafraîchir"; +"REFRESHING_DATA" = "Rafraîchissement de données"; +"REINSTALL" = "Réinstaller"; +"RELOAD" = "Recharger"; +"RELOAD_SPRINGBOARD" = "Relancer SpringBoard"; +"RELOADING_DATA" = "Rechargement des données"; +"REMOVE" = "Supprimer"; +"REMOVING_ESSENTIALS" = "Suppression d'essentiels"; +"REMOVING_ESSENTIALS_EX" = "Cette opération va supprimer un ou plusieurs paquets indispensables au bon fonctionnement de Cydia ou de votre iPhone. Si vous continuez, vous pourriez bien ne pas pouvoir utiliser Cydia pour réparer les dommages causés."; +"REPAIRING" = "Réparation"; +"REPOSITORY_SIGNATURE" = "Voir la Signature de la Source"; +"REQUEST_ADVERTISING_SPACE" = "Demander un Espace de Publicité"; +"REQUIREMENTS_FOR_LISTING" = "Prérequis pour le Listage"; +"RESTART_SPRINGBOARD" = "Relancer le SpringBoard"; +"RESUBMIT_FORM" = "Êtes-vous certain(e) de vouloir envoyer ce formulaire à nouveau?"; +"RESUMING_AT" = "Reprise à"; +"RETURN_TO_CYDIA" = "Retour à Cydia"; +"ROLE_EX" = "Certains des paquets disponibles sur Cydia ne sont pas destinés à tous les utilisateurs. Choisissez votre catégorie pour que Cydia ne vous propose que ceux dont vous auriez besoin.\n\nCe choix peut être changé dans les \"Réglages\" du menu \"Gestion\"."; +"RUNNING" = "En Fonction"; +"SAFE" = "Sûr"; +"SAMPLE" = "Échantillon"; +"SCREENSHOT" = "Capture d'écran"; +"SCREENSHOTS" = "Captures d'écran"; +"SEARCH" = "Recherche"; +"SEARCH_EX" = "Noms des paquets & descriptions"; +"SECTION" = "Catégorie"; +"SECTION_VISIBILITY" = "Visibilité des catégories"; +"SECTIONS" = "Catégories"; +"SEE_WHAT_CHANGED" = "Voyez ce qui a changé"; +"SETTINGS" = "Réglages"; +"SHOW_ALL_CHANGES" = "Montrer tous les changements"; +"SHOW_ALL_CHANGES_EX" = "Seuls les mises à jour de paquets installés ne sont affichés ici pour limiter le spam des empaqueteurs. En activant ceci vous serez averti des mises à jour sans que ce paquet soit installé."; +"SIGNED_PACKAGE" = "Ce paquet est signé."; +"SIMPLE" = "Simple"; +"SINGLE_QUOTED" = "‹ %@ ›"; +"SLASH_DELIMITED" = "%@ / %@"; +"SOURCE_DETAILS" = "Détails sur la source"; +"SOURCE_WARNING" = "Avertissement de source"; +"SOURCES" = "Sources"; +"SOURCES_EX" = "Voyez vos sources et en ajoutez des nouvelles."; +"SPONSOR" = "Commanditaire"; +"SPONSORING_PRODUCTS" = "Produits commanditants"; +"STATISTICS" = "Statistiques"; +"STILL_BROKEN_EX" = "Même après avoir essayé de réparé les sélections, le problème n'a pas l'air à être réglé."; +"STORAGE" = "Stockage"; +"STORAGE_EX" = "Voyez combien d'espace il vous reste pour de nouveaux paquets."; +"SUBMIT" = "Envoyer"; +"SUPPORT_KNOWN_ISSUES" = "Aide & problèmes connus"; +"TEMPORARY_IGNORE" = "Ignorer (temporairement)"; +"THANK_YOU" = "Remerciements"; +"QUEUE" = "File d'attente"; +"UNABLE_TO_COMPLY" = "Impossible"; +"UNABLE_TO_COMPLY_EX" = "Cette opération va supprimer un ou plusieurs paquets indispensables au bon fonctionnement de Cydia ou de votre iPhone. Pour continuer et forcer cette opération, vous devez activer le mode Utilisateur avancé dans les Réglages."; +"UNABLE_TO_LOAD" = "Chargement impossible"; +"UNKNOWN" = "Inconnu"; +"UNSAFE" = "Risqué"; +"UPDATING_DATABASE" = "Mise à jour de la base de données"; +"UPDATING_SOURCES" = "Mise à jour des sources"; +"UPGRADE" = "Mise à jour"; +"UPGRADE_ESSENTIAL" = "Mise à jour des essentiels"; +"UPGRADING_TO_READ_THIS" = "À lire: mise à jour à %@"; +"USER" = "Utilisateur"; +"USER_EX" = "GUI seulement"; +"USER_GUIDES" = "Guides utilisateur"; +"USERNAME" = "Nom d'utilisateur"; +"VERIFICATION_ERROR" = "Erreur de vérification"; +"VERIFYING_URL" = "Vérification de l'URL"; +"VERSION" = "Version"; +"WARNING" = "Avertissement"; +"WELCOME_TO_CYDIA" = "Bienvenue dans Cydia™"; +"WHO_ARE_YOU" = "Qui êtes-vous?"; +"YES" = "Oui"; diff --git a/Cydia.app/French.lproj/Sections.strings b/Cydia.app/French.lproj/Sections.strings new file mode 100644 index 0000000..799a82c --- /dev/null +++ b/Cydia.app/French.lproj/Sections.strings @@ -0,0 +1,45 @@ +"Administration" = "Administration"; +"App Addons" = "Ajouts aux applications"; +"Archiving" = "Archivage"; +"Battery" = "Batterie"; +"Carrier Bundles" = "Opérateurs"; +"Carriers" = "Opérateurs"; +"Complete" = "Terminé"; +"Data Storage" = "Archivage de données"; +"Development" = "Développement"; +"Dialer" = "Numéroteur"; +"Dictionaries" = "Dictionnaires"; +"Dock" = "Dock"; +"eBooks" = "eLivres"; +"Education" = "Éducation"; +"Emulation" = "Émulation"; +"Entertainment" = "Divertissement"; +"Games" = "Jeux"; +"Icy" = "Icy"; +"Java" = "Java"; +"Keyboards" = "Claviers"; +"Localization" = "Localisation"; +"LockScreen" = "Écran de verrouillage"; +"Messaging" = "Messagerie"; +"Multimedia" = "Multimédia"; +"Networking" = "Réseau"; +"Notifications" = "Notifications"; +"Packaging" = "Enpaquetage"; +"Productivity" = "Productivité"; +"Repositories" = "Sources"; +"Ringtones" = "Sonneries"; +"Scripting" = "Scripts"; +"Security" = "Sécurité"; +"Sliders" = "Sliders"; +"SMS" = "SMS"; +"System" = "Système"; +"Terminal Support" = "Aide au terminal"; +"Text Editors" = "Éditeurs de texte"; +"Themes" = "Thèmes"; +"Toys" = "Jouets"; +"Tweaks" = "Modifications"; +"Utilities" = "Utilitaires"; +"Wallpaper" = "Fonds d'écran"; +"Weather" = "Météo"; +"WebClips" = "WebClips"; +"Widgets" = "Widgets"; diff --git a/Cydia.app/German.lproj/Localizable.strings b/Cydia.app/German.lproj/Localizable.strings new file mode 100644 index 0000000..78a818c --- /dev/null +++ b/Cydia.app/German.lproj/Localizable.strings @@ -0,0 +1,210 @@ +"ABOUT" = "Über"; +"ABOUT_CYDIA" = "Über Cydia Installer"; +"ACCEPT_NEW_COPY" = "Die neue Kopie annehmen"; +"ADD" = "Hinzufügen"; +"ADD_ANYWAY" = "Trotzdem hinzufügen"; +"ADD_SOURCE" = "Quelle hinzufügen"; +"ADMINISTRATIVE_INFORMATION" = "Administrative Information"; +"ADVANCED_SEARCH" = "Erweiterte Suche"; +"ALL_PACKAGES" = "Alle Pakete"; +"APPLE" = "Apple"; +"AUTHOR" = "Autor"; +"AVAILABLE_UPGRADES" = "Vorhandene Aktualisierungen"; +"BROWSER" = "Browser"; +"BY" = "von %@"; +"CANCEL" = "Abbrechen"; +"CANCEL_CLEAR" = "Abbrechen und leeren"; +"CANCEL_OPERATION" = "Vorgang abbrechen"; +"CANNOT_COMPLY" = "Ausführung unmöglich"; +"CANNOT_COMPLY_EX" = "Die gewünschten Änderungen können wegen notwendiger Abhängigkeiten oder Konflikten, die nicht automatisch behoben werden können, nicht durchgeführt werden."; +"CANNOT_LOCATE_PACKAGE" = "Paket nicht gefunden"; +"CHANGE_PACKAGE_SETTINGS" = "Paket-Einstellungen ändern"; +"CHANGES" = "Änderungen"; +"CLEAN_ARCHIVES" = "Archive aufräumen"; +"CLEAR" = "Leeren"; +"CLOSE" = "Schließen"; +"CLOSE_CYDIA" = "Cydia beenden (neustarten)"; +"COLON_DELIMITED" = "%@: %@"; +"COMMA_DELIMITED" = "%@, %@"; +"COMING_SOON" = "Bald verfügbar!"; +"COMPLETE" = "Fertig"; +"COMPLETE_UPGRADE" = "Vollständige Aktualisierung"; +"CONFIGURATION_UPGRADE" = "Konfiguration wählen"; +"CONFIGURATION_UPGRADE_EX" = "Die folgende Datei wurde sowohl durch den Paket-Inhaber als auch von Ihnen (oder für Sie von einem Skript) geändert."; +"COMMERCIAL_APPLICATIONS" = "Kommerzielle Programme"; +"CONFIRM" = "Bestätigen"; +"CONSOLE_PACKAGE" = "Dies ist ein Konsolen-Paket!"; +"CONSOLE_UTILITIES_DAEMONS" = "Konsolentools & Hintergrundprozesse"; +"CONTINUE_QUEUING" = "Weiter auswählen"; +"COUNTS_NONZERO_EX" = "So, ich habe gerade ein paar wichtig scheinende Werte gezählt, und ihre Summe war nicht null. Das ist beängstigend. Ich weiß nicht mehr, warum ich das nicht mag, aber ich denke APT bestand darauf, dass das nicht passieren darf."; +"CREDITS" = "Credits"; +"CYDIA_STORE" = "Cydia Store"; +"DATABASE" = "Datenbank"; +"DETAILS" = "Details"; +"DEVELOPER" = "Entwickler"; +"DEVELOPER_EX" = "Ohne Filter"; +"DEVELOPERS_ONLY" = "Nur für Entwickler"; +"DISK_FREEING" = "Freigegebener Speicher"; +"DISK_USING" = "Speicherverbrauch"; +"DONATE_USING" = "Spenden via %@"; +"DONE" = "Fertig"; +"DOUBLE_QUOTED" = "„%@“"; +"DOWNGRADE" = "Downgrade"; +"DOWNLOADING" = "Lädt"; +"DOWNLOADING_" = "Lädt %@ herunter"; +"EDIT" = "Bearbeiten"; +"ENTER_APT_URL" = "Cydia/APT URL eingeben"; +"ENTERED_BY_USER" = "Vom Benutzer eingegeben"; +"ERROR" = "Fehler"; +"ESSENTIAL_UPGRADE" = "Wichtige Aktualisierung"; +"ESSENTIAL_UPGRADES" = "%d wichtige Aktualisierungen"; +"ESSENTIAL_UPGRADE_EX" = "Ein oder mehr wichtige Pakete sind veraltet. Um Fehler zu vermeiden, wird empfohlen die Aktualisierungen zu installieren."; +"EXCLAMATION" = "%@!"; +"EXPANDED_SIZE" = "Entpackte Größe"; +"EXPERT" = "Experte"; +"FEATURED_PACKAGES" = "Paketempfehlungen"; +"FAQ" = "FAQ"; +"FILES" = "Dateien"; +"FILES_INSTALLED_TO" = "Dateien installiert nach %@"; +"FILESYSTEM_CONTENT" = "Inhalt des Dateisystems"; +"FOLLOW_ON_TWITTER" = "Folge %@ auf Twitter"; +"FORCE_REMOVAL" = "Löschen erzwingen"; +"FORCIBLY_CLEAR" = "Leeren erzwingen"; +"FREE_APPLICATIONS" = "Kostenlose Programme"; +"FREE_EXTENSIONS" = "Kostenlose Erweiterungen"; +"FREQUENTLY_ASKED_QUESTIONS" = "Häufig gestellte Fragen (FAQ)"; +"FROM" = "von %@"; +"FROWNY_PANTS" = "Stirnrunzelnde Hose"; +"FUTURE_FEATURE_ROADMAP" = "Übersicht geplanter Funktionen"; +"HACKER" = "Hacker"; +"HACKER_EX" = "+ Kommandozeile"; +"HALFINSTALLED_PACKAGE" = "Teilinstalliertes Paket"; +"HALFINSTALLED_PACKAGES" = "%d teilinstallierte Pakete"; +"HALFINSTALLED_PACKAGE_EX" = "Wenn die an Pakete gebundenen Skripte Probleme machen, werden sie in einem schlechten Status (halb eingestellt oder halb installiert) zurückgelassen. Diese Fehler sind dauerhaft und erzeugen weitere Probleme. Diese fehlerhaften Skripte und Pakete können entfernt werden."; +"HOME" = "Start"; +"ID" = "ID"; +"IGNORE_UPGRADES" = "Aktualisierungen ignorieren"; +"IGNORED_UPGRADES" = "Ignorierte Aktualisierungen"; +"ILLEGAL_PACKAGE_IDENTIFIER" = "Unerlaubte Paket-Kennung"; +"INSTALL" = "Installieren"; +"INSTALLED" = "Installiert"; +"INSTALLED_BY_PACKAGE" = "Installiert nach Paket"; +"INSTALLED_DETAILS" = "Installiertes Paket"; +"INSTALLED_FILES" = "Installierte Dateien"; +"KEEP_OLD_COPY" = "Meine alte Kopie behalten"; +"LOADING" = "Lade"; +"LOADING_DATA" = "Lade Daten"; +"LOCAL" = "Lokal"; +"LOGIN" = "Anmeldung"; +"MAILING_LISTS" = "Mailingliste"; +"MAINTAINER" = "Kontakt"; +"MANAGE" = "Verwalten"; +"MODIFICATIONS" = "Änderungen"; +"MODIFY" = "Verändern"; +"MORE_INFORMATION" = "Mehr Informationen"; +"MORE_PACKAGE_SOURCES" = "Mehr Paketquellen"; +"NAME" = "Name"; +"NETWORK_ERROR" = "Netzwerk-Fehler"; +"NEVER_OR_UNKNOWN" = "Nie oder unbekannt"; +"NEW" = "Neu"; +"NEW_AT" = "Neu am %@"; +"NEW_INSTALL" = "Neuinstallation"; +"NO" = "Nein"; +"NO_SECTION" = "(Keine Kategorie)"; +"NOT_RATED" = "Nicht bewertet"; +"NOTE" = "Hinweis"; +"NOT_REPOSITORY" = "Quelle nicht gefunden"; +"NOT_REPOSITORY_EX" = "Die angegebene Quelle konnte nicht gefunden werden. Dies kann passieren, wenn eine Installer-Quelle hinzugefügt wurde (diese werden nicht unterstützt). Außerdem kann dieses Programm nur mit exakten, vollständigen URLs umgehen. Falls sie eine Quelle hosten und Probleme haben kontaktieren sie mit ihren Fragen bitte den Autor von Cydia."; +"OK" = "OK"; +"OKAY" = "Okay"; +"PACKAGE_CANNOT_BE_FOUND" = "Das Paket %@ kann in den aktuellen Quellen nicht gefunden werden. Ich schlage vor, mehr Quellen hinzuzufügen."; +"PACKAGE_CANNOT_BE_FOUND_EX" = "Dieses Problem kann auch durch eine Reihe anderer Faktoren hervorgerufen worden sein. Der häufigste ist, dass dein Paketkatalog nicht mehr aktuell ist. Zum manuellen erneuern, klicke den Aktualisieren-Knopf im Änderungen-Tab. Das Paket könnte auch einfach nicht mehr existieren. Es könnte auch sein, dass du eine alte Firmwareversion nutzt: Nutzer sollten versuchen halbwegs auf dem Laufenden zu bleiben mit Apples neuen Firmwares."; +"PACKAGE_DAMAGED" = "Paket beschädigt"; +"PACKAGE_DETAILS" = "Paketdetails"; +"PACKAGE_QUEUE" = "Paket-Warteschlange"; +"PACKAGES" = "Pakete"; +"PACKAGES_EX" = "Bereits installierte Pakete ansehen oder löschen."; +"PARENTHETICAL" = "%@ (%@)"; +"PASSWORD" = "Passwort"; +"PERFORM_SELECTIONS" = "Führe Auswahl durch"; +"PREPARE_ARCHIVES" = "Bereite Archive vor"; +"PRODUCTS" = "Produkte"; +"PURGE" = "Völlig entfernen"; +"Q_D" = "S"; +"QUEUED_FOR" = "In Warteschlange für %@"; +"QUESTION" = "%@?"; +"REBOOT_DEVICE" = "Gerät neustarten"; +"RECENT_CHANGES_UPDATES" = "Neueste Änderungen/Aktualisierungen"; +"RECOMMENDED_BOOKS" = "Empfohlene Bücher"; +"REFRESH" = "Aktualisieren"; +"REFRESHING_DATA" = "Hole neue Daten"; +"REINSTALL" = "Neu installieren"; +"RELOAD" = "Neu laden"; +"RELOAD_SPRINGBOARD" = "SpringBoard neu laden"; +"RELOADING_DATA" = "Lade neue Daten"; +"REMOVE" = "Entfernen"; +"REMOVING_ESSENTIALS" = "Grundlegende Pakete löschen"; +"REMOVING_ESSENTIALS_EX" = "Diese Aktion erfordert das Löschen eines oder mehrerer Pakete die notwendig für die Funktionalität von Cydia oder iPhoneOS sind. Wenn Sie fortsetzen, können Sie ggf. Cydia nicht mehr benutzen um den Schaden zu beheben."; +"REPAIRING" = "Reparieren"; +"REPOSITORY_SIGNATURE" = "Quellen-Signatur anzeigen"; +"REQUEST_ADVERTISING_SPACE" = "Werbefläche beantragen"; +"REQUIREMENTS_FOR_LISTING" = "Bedingungen für die Aufnahme"; +"RESTART_SPRINGBOARD" = "SpringBoard neu starten"; +"RESUBMIT_FORM" = "Sind Sie sicher, dass Sie das Formular erneut senden möchten?"; +"RESUMING_AT" = "Wiederaufnahme bei"; +"RETURN_TO_CYDIA" = "Zu Cydia zurückkehren"; +"ROLE_EX" = "Nicht alle in Cydia erhältlichen Pakete sind für alle Benutzer gedacht. Bitte stufen Sie sich ein, so dass Cydia hilfreiche Filter anwenden kann.\n\nDiese Einstellung kann in den \"Einstellungen\" unter \"Verwalten\" geändert werden."; +"RUNNING" = "Aktiv"; +"SAFE" = "Sicher"; +"SAMPLE" = "Beispiel"; +"SCREENSHOT" = "Bildschirmfoto"; +"SCREENSHOTS" = "Bildschirmfotos"; +"SEARCH" = "Suche"; +"SEARCH_EX" = "Paketname & Beschreibung"; +"SECTION" = "Kategorie"; +"SECTION_VISIBILITY" = "Sichtbarkeit der Kategorien"; +"SECTIONS" = "Kategorien"; +"SEE_WHAT_CHANGED" = "Änderungen ansehen"; +"SETTINGS" = "Einstellungen"; +"SHOW_ALL_CHANGES" = "Alle Änderungen zeigen"; +"SHOW_ALL_CHANGES_EX" = "Änderungen zeigt nur Aktualisierungen der installierten Paketen an, um Spam von Paket-Herstellern zu reduzieren. Aktivieren Sie dies, um Aktualisierungen zu diesem Paket zu sehen, obwohl es nicht installiert ist."; +"SIGNED_PACKAGE" = "Dieses Paket wurde signiert."; +"SIMPLE" = "Einfach"; +"SINGLE_QUOTED" = "‚%@‘"; +"SLASH_DELIMITED" = "%@ / %@"; +"SOURCE_DETAILS" = "Information zur Quelle"; +"SOURCE_WARNING" = "Quellenwarnung"; +"SOURCES" = "Quellen"; +"SOURCES_EX" = "Quellenliste anzeigen und eigene hinzufügen."; +"SPONSOR" = "Sponsor"; +"SPONSORING_PRODUCTS" = "Unterstützende Produkte"; +"STATISTICS" = "Statistik"; +"STILL_BROKEN_EX" = "Der Versuch, die kaputten Sektionen zu reparieren hat nichts gebracht."; +"STORAGE" = "Speicherplatz"; +"STORAGE_EX" = "Nachsehen, wieviel Speicherplatz für neue Pakete vorhanden ist."; +"SUBMIT" = "Absenden"; +"SUPPORT_KNOWN_ISSUES" = "Support & bekannte Probleme"; +"TEMPORARY_IGNORE" = "Ignorieren (temporär)"; +"THANK_YOU" = "Danke"; +"QUEUE" = "Warteschlange"; +"UNABLE_TO_COMPLY" = "Operation nicht möglich"; +"UNABLE_TO_COMPLY_EX" = "Diese Aktion erfordert das Löschen eines oder mehrerer Pakete die notwendig für die Funktionalität von Cydia oder iPhoneOS sind. Um fortzufahren und diese Aktion zu erzwingen muss der Fortgeschrittenen-Modus in den Einstellungen aktiviert sein."; +"UNABLE_TO_LOAD" = "Laden nicht möglich"; +"UNKNOWN" = "Unbekannt"; +"UNSAFE" = "Unsicher"; +"UPDATING_DATABASE" = "Datenbank aktualisieren"; +"UPDATING_SOURCES" = "Quellen aktualisieren"; +"UPGRADE" = "Aktualisierung"; +"UPGRADE_ESSENTIAL" = "Wichtige Pakete aktualisieren"; +"UPGRADING_TO_READ_THIS" = "Upgrade auf %@? Hier lesen"; +"USER" = "Benutzer"; +"USER_EX" = "Nur Grafische"; +"USER_GUIDES" = "Benutzer-Handbücher"; +"USERNAME" = "Benutzername"; +"VERIFICATION_ERROR" = "Fehler beim Überprüfen"; +"VERIFYING_URL" = "URL überprüfen"; +"VERSION" = "Version"; +"WARNING" = "Warnung"; +"WELCOME_TO_CYDIA" = "Willkommen bei Cydia™"; +"WHO_ARE_YOU" = "Wer sind Sie?"; +"YES" = "Ja"; diff --git a/Cydia.app/German.lproj/Sections.strings b/Cydia.app/German.lproj/Sections.strings new file mode 100644 index 0000000..6abe4ce --- /dev/null +++ b/Cydia.app/German.lproj/Sections.strings @@ -0,0 +1,49 @@ +"Addons" = "Erweiterungen"; +"Administration" = "Verwaltung"; +"Adult" = "Ab 18"; +"App Addons" = "Programm-Erweiterugen"; +"Archiving" = "Archivierung"; +"Battery" = "Batterie"; +"Carrier Bundles" = "Telefongesellschaft-Pakete"; +"Carriers" = "Telefongesellschaften"; +"Complete" = "Vollständig"; +"Data Storage" = "Speicherplatzverwaltung"; +"Development" = "Entwicklung"; +"Dialer" = "Wählhilfen"; +"Dictionaries" = "Wörterbücher"; +"Dock" = "Dock"; +"eBooks" = "eBooks"; +"Education" = "Bildung"; +"Emulation" = "Emulation"; +"Entertainment" = "Unterhaltung"; +"Games" = "Spiele"; +"Icy" = "Icy"; +"Java" = "Java"; +"Keyboards" = "Tastaturen"; +"Localization" = "Lokalisierung"; +"LockScreen" = "Lockscreen"; +"Messaging" = "Nachrichtendienste"; +"Multimedia" = "Multimedia"; +"Networking" = "Netzwerk"; +"Notifications" = "Mitteilugen"; +"Packaging" = "Paketverwaltung"; +"Productivity" = "Produktivität / Kreativität"; +"Repositories" = "Quellen"; +"Ringtones" = "Klingeltöne"; +"Scripting" = "Skriptsprachen"; +"Security" = "Sicherheit"; +"Sliders" = "Slider"; +"SMS" = "SMS"; +"Social" = "Soziale Netze"; +"System" = "System"; +"Terminal Support" = "Konsolen-Support"; +"Text Editors" = "Texteditoren"; +"Themes" = "Themes"; +"Toys" = "Spielereien"; +"Tweaks" = "Tweaks / Tuning"; +"Utilities" = "Dienstprogramme"; +"Videos" = "Videos"; +"Wallpaper" = "Hintergrundbilder"; +"Weather" = "Wetter"; +"WebClips" = "Web-Schnipsel"; +"Widgets" = "Widgets"; diff --git a/Cydia.app/Icon-Small.png b/Cydia.app/Icon-Small.png new file mode 100644 index 0000000..9d591e7 Binary files /dev/null and b/Cydia.app/Icon-Small.png differ diff --git a/Cydia.app/Info.plist b/Cydia.app/Info.plist new file mode 100644 index 0000000..a8cf2eb --- /dev/null +++ b/Cydia.app/Info.plist @@ -0,0 +1,75 @@ + + + + + + CFBundleDevelopmentRegion + en + + CFBundleExecutable + Cydia + + CFBundleIdentifier + com.saurik.Cydia + + CFBundleInfoDictionaryVersion + 6.0 + + CFBundleName + Cydia + + CFBundlePackageType + APPL + + CFBundleShortVersionString + 0.9 + + CFBundleSignature + ???? + + CFBundleVersion + 0.9 + + SBUsesNetwork + 11 + + CFBundleURLTypes + + + CFBundleURLName + Cydia Internal URL + CFBundleURLSchemes + + cydia + + + + + DTSDKName + iphoneos2.0.saurik + + MinimumOSVersion + 2.0 + + UIDeviceFamily + + 1 + 2 + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + + + UIBackgroundModes + + audio + continuous + + + + diff --git a/Cydia.app/Modes/DOWNGRADE.png b/Cydia.app/Modes/DOWNGRADE.png new file mode 100644 index 0000000..41c0d70 Binary files /dev/null and b/Cydia.app/Modes/DOWNGRADE.png differ diff --git a/Cydia.app/Modes/INSTALL.png b/Cydia.app/Modes/INSTALL.png new file mode 100644 index 0000000..a83f354 Binary files /dev/null and b/Cydia.app/Modes/INSTALL.png differ diff --git a/Cydia.app/Modes/NEW_INSTALL.png b/Cydia.app/Modes/NEW_INSTALL.png new file mode 120000 index 0000000..475bda6 --- /dev/null +++ b/Cydia.app/Modes/NEW_INSTALL.png @@ -0,0 +1 @@ +INSTALL.png \ No newline at end of file diff --git a/Cydia.app/Modes/PURGE.png b/Cydia.app/Modes/PURGE.png new file mode 100644 index 0000000..795fa19 Binary files /dev/null and b/Cydia.app/Modes/PURGE.png differ diff --git a/Cydia.app/Modes/REINSTALL.png b/Cydia.app/Modes/REINSTALL.png new file mode 100644 index 0000000..0b7754c Binary files /dev/null and b/Cydia.app/Modes/REINSTALL.png differ diff --git a/Cydia.app/Modes/REMOVE.png b/Cydia.app/Modes/REMOVE.png new file mode 100644 index 0000000..1b2e691 Binary files /dev/null and b/Cydia.app/Modes/REMOVE.png differ diff --git a/Cydia.app/Modes/UPGRADE.png b/Cydia.app/Modes/UPGRADE.png new file mode 100644 index 0000000..631b7ef Binary files /dev/null and b/Cydia.app/Modes/UPGRADE.png differ diff --git a/Cydia.app/Purposes/commercial.png b/Cydia.app/Purposes/commercial.png new file mode 100644 index 0000000..ba66d4b Binary files /dev/null and b/Cydia.app/Purposes/commercial.png differ diff --git a/Cydia.app/Purposes/console.png b/Cydia.app/Purposes/console.png new file mode 100644 index 0000000..48f7e88 Binary files /dev/null and b/Cydia.app/Purposes/console.png differ diff --git a/Cydia.app/Purposes/daemon.png b/Cydia.app/Purposes/daemon.png new file mode 100644 index 0000000..4593ee8 Binary files /dev/null and b/Cydia.app/Purposes/daemon.png differ diff --git a/Cydia.app/Purposes/extension.png b/Cydia.app/Purposes/extension.png new file mode 100644 index 0000000..e7025b4 Binary files /dev/null and b/Cydia.app/Purposes/extension.png differ diff --git a/Cydia.app/Purposes/library.png b/Cydia.app/Purposes/library.png new file mode 100644 index 0000000..3bcbc04 Binary files /dev/null and b/Cydia.app/Purposes/library.png differ diff --git a/Cydia.app/Purposes/uikit.png b/Cydia.app/Purposes/uikit.png new file mode 100644 index 0000000..2141d72 Binary files /dev/null and b/Cydia.app/Purposes/uikit.png differ diff --git a/Cydia.app/Purposes/x.png b/Cydia.app/Purposes/x.png new file mode 100644 index 0000000..0b24b74 Binary files /dev/null and b/Cydia.app/Purposes/x.png differ diff --git a/Cydia.app/Sections.plist b/Cydia.app/Sections.plist new file mode 100644 index 0000000..fb590b6 --- /dev/null +++ b/Cydia.app/Sections.plist @@ -0,0 +1,145 @@ + + + + + + Hunspell_Dictionaries + + Rename + Dictionaries_(Hunspell) + + + Utility + + Rename + Utilities + + + Themes_(Springboard) + + Rename + Themes_(SpringBoard) + + + Themes_(SummerBoard) + + Rename + Themes_(SpringBoard) + + + Intelliborn + + Rename + Utilities + + + SMS + + Rename + Messaging + + + CarrierBundle + + Rename + Carrier_Bundles + + + Messages + + Rename + Messaging + + + Themes_(Locksceen) + + Rename + Themes_(LockScreen) + + + Themes_(Lockscree) + + Rename + Themes_(LockScreen) + + + Themes_(Losckscreen) + + Rename + Themes_(LockScreen) + + + Themes_(Video) + + Rename + Themes_(Videos) + + + Utilites + + Rename + Utilities + + + Themes_(Lockscreen) + + Rename + Themes_(LockScreen) + + + Themes_(Complet) + + Rename + Themes_(Complete) + + + Wallpapers + + Rename + Wallpaper + + + Messageing + + Rename + Messaging + + + Network + + Rename + Networking + + + Tweak + + Rename + Tweaks + + + Tweaks_2.0 + + Rename + Tweaks_(2.0) + + + Tweaks_2.1 + + Rename + Tweaks_(2.1) + + + Tweaks_2.2 + + Rename + Tweaks_(2.2) + + + Tweaks_3.0 + + Rename + Tweaks_(3.0) + + + + diff --git a/Cydia.app/Sections/Administration.png b/Cydia.app/Sections/Administration.png new file mode 100644 index 0000000..65efd3e Binary files /dev/null and b/Cydia.app/Sections/Administration.png differ diff --git a/Cydia.app/Sections/App Addons.png b/Cydia.app/Sections/App Addons.png new file mode 100644 index 0000000..424f565 Binary files /dev/null and b/Cydia.app/Sections/App Addons.png differ diff --git a/Cydia.app/Sections/Archiving.png b/Cydia.app/Sections/Archiving.png new file mode 100644 index 0000000..9efce56 Binary files /dev/null and b/Cydia.app/Sections/Archiving.png differ diff --git a/Cydia.app/Sections/Blanks.png b/Cydia.app/Sections/Blanks.png new file mode 100644 index 0000000..7c0c2a0 Binary files /dev/null and b/Cydia.app/Sections/Blanks.png differ diff --git a/Cydia.app/Sections/Carrier Bundles.png b/Cydia.app/Sections/Carrier Bundles.png new file mode 100644 index 0000000..00f36f7 Binary files /dev/null and b/Cydia.app/Sections/Carrier Bundles.png differ diff --git a/Cydia.app/Sections/Communication.png b/Cydia.app/Sections/Communication.png new file mode 100644 index 0000000..00f36f7 Binary files /dev/null and b/Cydia.app/Sections/Communication.png differ diff --git a/Cydia.app/Sections/Data Storage.png b/Cydia.app/Sections/Data Storage.png new file mode 100644 index 0000000..1bb23fe Binary files /dev/null and b/Cydia.app/Sections/Data Storage.png differ diff --git a/Cydia.app/Sections/Development.png b/Cydia.app/Sections/Development.png new file mode 100644 index 0000000..806b442 Binary files /dev/null and b/Cydia.app/Sections/Development.png differ diff --git a/Cydia.app/Sections/Dictionaries.png b/Cydia.app/Sections/Dictionaries.png new file mode 100644 index 0000000..e386e76 Binary files /dev/null and b/Cydia.app/Sections/Dictionaries.png differ diff --git a/Cydia.app/Sections/Education.png b/Cydia.app/Sections/Education.png new file mode 100644 index 0000000..4a2a03f Binary files /dev/null and b/Cydia.app/Sections/Education.png differ diff --git a/Cydia.app/Sections/Entertainment.png b/Cydia.app/Sections/Entertainment.png new file mode 100644 index 0000000..69c8214 Binary files /dev/null and b/Cydia.app/Sections/Entertainment.png differ diff --git a/Cydia.app/Sections/Games.png b/Cydia.app/Sections/Games.png new file mode 100644 index 0000000..6e14c50 Binary files /dev/null and b/Cydia.app/Sections/Games.png differ diff --git a/Cydia.app/Sections/Health and Fitness.png b/Cydia.app/Sections/Health and Fitness.png new file mode 100644 index 0000000..e2db013 Binary files /dev/null and b/Cydia.app/Sections/Health and Fitness.png differ diff --git a/Cydia.app/Sections/Imaging.png b/Cydia.app/Sections/Imaging.png new file mode 100644 index 0000000..1e8f7fa Binary files /dev/null and b/Cydia.app/Sections/Imaging.png differ diff --git a/Cydia.app/Sections/Java.png b/Cydia.app/Sections/Java.png new file mode 100644 index 0000000..e6bbc91 Binary files /dev/null and b/Cydia.app/Sections/Java.png differ diff --git a/Cydia.app/Sections/Keyboards.png b/Cydia.app/Sections/Keyboards.png new file mode 100644 index 0000000..6b3df89 Binary files /dev/null and b/Cydia.app/Sections/Keyboards.png differ diff --git a/Cydia.app/Sections/Localization.png b/Cydia.app/Sections/Localization.png new file mode 100644 index 0000000..80d41d3 Binary files /dev/null and b/Cydia.app/Sections/Localization.png differ diff --git a/Cydia.app/Sections/Messaging.png b/Cydia.app/Sections/Messaging.png new file mode 100644 index 0000000..69d18e0 Binary files /dev/null and b/Cydia.app/Sections/Messaging.png differ diff --git a/Cydia.app/Sections/Multimedia.png b/Cydia.app/Sections/Multimedia.png new file mode 100644 index 0000000..42d0b5e Binary files /dev/null and b/Cydia.app/Sections/Multimedia.png differ diff --git a/Cydia.app/Sections/Navigation.png b/Cydia.app/Sections/Navigation.png new file mode 100644 index 0000000..4829e62 Binary files /dev/null and b/Cydia.app/Sections/Navigation.png differ diff --git a/Cydia.app/Sections/Networking.png b/Cydia.app/Sections/Networking.png new file mode 100644 index 0000000..a4f04e3 Binary files /dev/null and b/Cydia.app/Sections/Networking.png differ diff --git a/Cydia.app/Sections/Packaging.png b/Cydia.app/Sections/Packaging.png new file mode 100644 index 0000000..d7d914e Binary files /dev/null and b/Cydia.app/Sections/Packaging.png differ diff --git a/Cydia.app/Sections/Planet-iPhones Mods.png b/Cydia.app/Sections/Planet-iPhones Mods.png new file mode 100644 index 0000000..e641970 Binary files /dev/null and b/Cydia.app/Sections/Planet-iPhones Mods.png differ diff --git a/Cydia.app/Sections/Productivity.png b/Cydia.app/Sections/Productivity.png new file mode 100644 index 0000000..c99c69d Binary files /dev/null and b/Cydia.app/Sections/Productivity.png differ diff --git a/Cydia.app/Sections/Repositories.png b/Cydia.app/Sections/Repositories.png new file mode 100644 index 0000000..d6fe267 Binary files /dev/null and b/Cydia.app/Sections/Repositories.png differ diff --git a/Cydia.app/Sections/Ringtones.png b/Cydia.app/Sections/Ringtones.png new file mode 100644 index 0000000..57fd660 Binary files /dev/null and b/Cydia.app/Sections/Ringtones.png differ diff --git a/Cydia.app/Sections/SBSettings Addons.png b/Cydia.app/Sections/SBSettings Addons.png new file mode 100644 index 0000000..571555a Binary files /dev/null and b/Cydia.app/Sections/SBSettings Addons.png differ diff --git a/Cydia.app/Sections/Scripting.png b/Cydia.app/Sections/Scripting.png new file mode 100644 index 0000000..22285d2 Binary files /dev/null and b/Cydia.app/Sections/Scripting.png differ diff --git a/Cydia.app/Sections/Security.png b/Cydia.app/Sections/Security.png new file mode 100644 index 0000000..ae142eb Binary files /dev/null and b/Cydia.app/Sections/Security.png differ diff --git a/Cydia.app/Sections/Social.png b/Cydia.app/Sections/Social.png new file mode 100644 index 0000000..6c0cfa4 Binary files /dev/null and b/Cydia.app/Sections/Social.png differ diff --git a/Cydia.app/Sections/System.png b/Cydia.app/Sections/System.png new file mode 100644 index 0000000..19d2c22 Binary files /dev/null and b/Cydia.app/Sections/System.png differ diff --git a/Cydia.app/Sections/Terminal Support.png b/Cydia.app/Sections/Terminal Support.png new file mode 100644 index 0000000..d664618 Binary files /dev/null and b/Cydia.app/Sections/Terminal Support.png differ diff --git a/Cydia.app/Sections/Text Editors.png b/Cydia.app/Sections/Text Editors.png new file mode 100644 index 0000000..5e65d46 Binary files /dev/null and b/Cydia.app/Sections/Text Editors.png differ diff --git a/Cydia.app/Sections/Themes.png b/Cydia.app/Sections/Themes.png new file mode 100644 index 0000000..e35b948 Binary files /dev/null and b/Cydia.app/Sections/Themes.png differ diff --git a/Cydia.app/Sections/Toys.png b/Cydia.app/Sections/Toys.png new file mode 100644 index 0000000..368eb4c Binary files /dev/null and b/Cydia.app/Sections/Toys.png differ diff --git a/Cydia.app/Sections/Tweaks.png b/Cydia.app/Sections/Tweaks.png new file mode 100644 index 0000000..4f588c6 Binary files /dev/null and b/Cydia.app/Sections/Tweaks.png differ diff --git a/Cydia.app/Sections/Utilities.png b/Cydia.app/Sections/Utilities.png new file mode 100644 index 0000000..fab5d32 Binary files /dev/null and b/Cydia.app/Sections/Utilities.png differ diff --git a/Cydia.app/Sections/Wallpaper.png b/Cydia.app/Sections/Wallpaper.png new file mode 100644 index 0000000..1060fa0 Binary files /dev/null and b/Cydia.app/Sections/Wallpaper.png differ diff --git a/Cydia.app/Sections/WebClips.png b/Cydia.app/Sections/WebClips.png new file mode 100644 index 0000000..7ed4053 Binary files /dev/null and b/Cydia.app/Sections/WebClips.png differ diff --git a/Cydia.app/Sections/Widgets.png b/Cydia.app/Sections/Widgets.png new file mode 100644 index 0000000..ca8c1ef Binary files /dev/null and b/Cydia.app/Sections/Widgets.png differ diff --git a/Cydia.app/Sections/X Window.png b/Cydia.app/Sections/X Window.png new file mode 100644 index 0000000..eac01e9 Binary files /dev/null and b/Cydia.app/Sections/X Window.png differ diff --git a/Cydia.app/Sections/eBooks.png b/Cydia.app/Sections/eBooks.png new file mode 100644 index 0000000..512c68b Binary files /dev/null and b/Cydia.app/Sections/eBooks.png differ diff --git a/Cydia.app/Sources/cy.sosiphone.com.png b/Cydia.app/Sources/cy.sosiphone.com.png new file mode 100644 index 0000000..b848d95 Binary files /dev/null and b/Cydia.app/Sources/cy.sosiphone.com.png differ diff --git a/Cydia.app/Sources/iphone.hackndev.org.png b/Cydia.app/Sources/iphone.hackndev.org.png new file mode 100644 index 0000000..1f701df Binary files /dev/null and b/Cydia.app/Sources/iphone.hackndev.org.png differ diff --git a/Cydia.app/Sources/iphonehe.com.png b/Cydia.app/Sources/iphonehe.com.png new file mode 100644 index 0000000..b20263e Binary files /dev/null and b/Cydia.app/Sources/iphonehe.com.png differ diff --git a/Cydia.app/Sources/urbanfanatics.com.png b/Cydia.app/Sources/urbanfanatics.com.png new file mode 100644 index 0000000..9cf47c4 Binary files /dev/null and b/Cydia.app/Sources/urbanfanatics.com.png differ diff --git a/Cydia.app/Sources/www.zodttd.com.png b/Cydia.app/Sources/www.zodttd.com.png new file mode 100644 index 0000000..83a3b33 Binary files /dev/null and b/Cydia.app/Sources/www.zodttd.com.png differ diff --git a/Cydia.app/Test b/Cydia.app/Test new file mode 100755 index 0000000..123c7eb --- /dev/null +++ b/Cydia.app/Test @@ -0,0 +1,9 @@ +#!/bin/bash +killall Cydia_ +set -e +rsync -SPaz --copy-links saurik@carrier.saurik.com:menes/cydia/cydia_iphoneos-arm.deb . +dpkg -i cydia_iphoneos-arm.deb +touch /tmp/cydia.log +chown mobile.mobile /tmp/cydia.log +uiopen "${1-cydia://}" +exec tail /var/log/syslog /tmp/cydia.log -fn0 diff --git a/Cydia.app/advanced.png b/Cydia.app/advanced.png new file mode 100644 index 0000000..419ba52 Binary files /dev/null and b/Cydia.app/advanced.png differ diff --git a/Cydia.app/changes.png b/Cydia.app/changes.png new file mode 100644 index 0000000..654208f Binary files /dev/null and b/Cydia.app/changes.png differ diff --git a/Cydia.app/changes@2x.png b/Cydia.app/changes@2x.png new file mode 100644 index 0000000..aa6a29a Binary files /dev/null and b/Cydia.app/changes@2x.png differ diff --git a/Cydia.app/chevron@2x.png b/Cydia.app/chevron@2x.png new file mode 100644 index 0000000..9905c17 Binary files /dev/null and b/Cydia.app/chevron@2x.png differ diff --git a/Cydia.app/commercial.png b/Cydia.app/commercial.png new file mode 100644 index 0000000..8599f48 Binary files /dev/null and b/Cydia.app/commercial.png differ diff --git a/Cydia.app/compose.png b/Cydia.app/compose.png new file mode 100644 index 0000000..1141d5d Binary files /dev/null and b/Cydia.app/compose.png differ diff --git a/Cydia.app/configure.png b/Cydia.app/configure.png new file mode 100644 index 0000000..c27618d Binary files /dev/null and b/Cydia.app/configure.png differ diff --git a/Cydia.app/confirm.html b/Cydia.app/confirm.html new file mode 100644 index 0000000..335a326 --- /dev/null +++ b/Cydia.app/confirm.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +
+

:

+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ +
+ diff --git a/Cydia.app/confirm.js b/Cydia.app/confirm.js new file mode 100644 index 0000000..ba2e2e5 --- /dev/null +++ b/Cydia.app/confirm.js @@ -0,0 +1,84 @@ +$(function () { + if (issues == null) { + $(".issues").remove(); + + var downloading = sizes[0]; + if (downloading == "0.0 B") + $(".downloading").remove(); + else + $("#downloading").html($.xml(downloading)); + + var resuming = sizes[1]; + if (resuming == "0.0 B") + $(".resuming").remove(); + else + $("#resuming").html($.xml(resuming)); + + var size = sizes[2]; + var negative; + + if (size.charAt(0) != '-') + negative = false; + else { + negative = true; + size = size.substr(1); + } + + $("#disk-key").html(cydia.localize(negative ? "DISK_FREEING" : "DISK_USING")); + $("#disk-value").html($.xml(size)); + } else for (var i = 0; i != issues.length; ++i) { + document.title = cydia.localize("CANNOT_COMPLY"); + + $("._issues").remove(); + + var issue = issues[i]; + + $("#issues").append( + "" + + "
" + ); + + for (var j = 1; j != issue.length; ++j) { + var entry = issue[j]; + var type = entry[0]; + if (type == "PreDepends") + type = "Depends"; + var version = entry[1]; + if (entry.length >= 4) + version += " " + entry[3]; + $("#i" + i).append("
" + + "" + + "" + + "
"); + } + } + + var keys = [ + "INSTALL", + "REINSTALL", + "UPGRADE", + "DOWNGRADE", + "REMOVE" + ]; + + for (var i = 0; i != 5; ++i) { + var list = changes[i]; + var length = list.length; + + if (length != 0) { + $("#modifications").append("
" + + "" + + "" + + "
"); + + var value = ""; + for (var j = 0; j != length; ++j) { + if (j != 0) + value += "
"; + value += $.xml(list[j]); + } + + $("#c" + i).html(value); + } + } +}); diff --git a/Cydia.app/console.png b/Cydia.app/console.png new file mode 100644 index 0000000..dba3788 Binary files /dev/null and b/Cydia.app/console.png differ diff --git a/Cydia.app/email.png b/Cydia.app/email.png new file mode 100644 index 0000000..1aa20b3 Binary files /dev/null and b/Cydia.app/email.png differ diff --git a/Cydia.app/error.html b/Cydia.app/error.html new file mode 100644 index 0000000..a68ff76 --- /dev/null +++ b/Cydia.app/error.html @@ -0,0 +1,45 @@ + + + + + + + + + + +
+ +
+ +
()
+ +
diff --git a/Cydia.app/expanded.png b/Cydia.app/expanded.png new file mode 100644 index 0000000..9efce56 Binary files /dev/null and b/Cydia.app/expanded.png differ diff --git a/Cydia.app/filesystem.png b/Cydia.app/filesystem.png new file mode 100644 index 0000000..1bb23fe Binary files /dev/null and b/Cydia.app/filesystem.png differ diff --git a/Cydia.app/folder.png b/Cydia.app/folder.png new file mode 100644 index 0000000..b628e95 Binary files /dev/null and b/Cydia.app/folder.png differ diff --git a/Cydia.app/folder@2x.png b/Cydia.app/folder@2x.png new file mode 100644 index 0000000..af3009c Binary files /dev/null and b/Cydia.app/folder@2x.png differ diff --git a/Cydia.app/green-dn.png b/Cydia.app/green-dn.png new file mode 100644 index 0000000..c311acd Binary files /dev/null and b/Cydia.app/green-dn.png differ diff --git a/Cydia.app/green-up.png b/Cydia.app/green-up.png new file mode 100644 index 0000000..c335b24 Binary files /dev/null and b/Cydia.app/green-up.png differ diff --git a/Cydia.app/home-Selected.png b/Cydia.app/home-Selected.png new file mode 100644 index 0000000..c02c120 Binary files /dev/null and b/Cydia.app/home-Selected.png differ diff --git a/Cydia.app/home-Selected@2x.png b/Cydia.app/home-Selected@2x.png new file mode 100644 index 0000000..748be33 Binary files /dev/null and b/Cydia.app/home-Selected@2x.png differ diff --git a/Cydia.app/home.png b/Cydia.app/home.png new file mode 100644 index 0000000..0611697 Binary files /dev/null and b/Cydia.app/home.png differ diff --git a/Cydia.app/home@2x.png b/Cydia.app/home@2x.png new file mode 100644 index 0000000..72b7297 Binary files /dev/null and b/Cydia.app/home@2x.png differ diff --git a/Cydia.app/icon-72.png b/Cydia.app/icon-72.png new file mode 100644 index 0000000..83a19f9 Binary files /dev/null and b/Cydia.app/icon-72.png differ diff --git a/Cydia.app/icon.png b/Cydia.app/icon.png new file mode 100644 index 0000000..384c582 Binary files /dev/null and b/Cydia.app/icon.png differ diff --git a/Cydia.app/icon@2x.png b/Cydia.app/icon@2x.png new file mode 100644 index 0000000..e9de3cb Binary files /dev/null and b/Cydia.app/icon@2x.png differ diff --git a/Cydia.app/id.png b/Cydia.app/id.png new file mode 100644 index 0000000..f168812 Binary files /dev/null and b/Cydia.app/id.png differ diff --git a/Cydia.app/install.png b/Cydia.app/install.png new file mode 100644 index 0000000..9e30898 Binary files /dev/null and b/Cydia.app/install.png differ diff --git a/Cydia.app/install@2x.png b/Cydia.app/install@2x.png new file mode 100644 index 0000000..dfb8212 Binary files /dev/null and b/Cydia.app/install@2x.png differ diff --git a/Cydia.app/installed.png b/Cydia.app/installed.png new file mode 100644 index 0000000..2f86f0a Binary files /dev/null and b/Cydia.app/installed.png differ diff --git a/Cydia.app/installed@2x.png b/Cydia.app/installed@2x.png new file mode 100644 index 0000000..ab86df2 Binary files /dev/null and b/Cydia.app/installed@2x.png differ diff --git a/Cydia.app/installing.png b/Cydia.app/installing.png new file mode 100644 index 0000000..6332fef Binary files /dev/null and b/Cydia.app/installing.png differ diff --git a/Cydia.app/loading.html b/Cydia.app/loading.html new file mode 100644 index 0000000..33b2637 --- /dev/null +++ b/Cydia.app/loading.html @@ -0,0 +1,15 @@ + + + Loading + + + + + +
+ +
+ Loading... +
+
+ diff --git a/Cydia.app/localize.js b/Cydia.app/localize.js new file mode 100644 index 0000000..9be82d0 --- /dev/null +++ b/Cydia.app/localize.js @@ -0,0 +1,15 @@ +document.addEventListener("DOMContentLoaded", function () { + var results = document.evaluate("//*[@localize]", document, null, XPathResult.ANY_TYPE, null); + var result, nodes = []; + while (result = results.iterateNext()) + nodes.push(result); + for (var index in nodes) { + var node = nodes[index]; + var key = node.getAttribute('localize'); + var value = cydia.localize(key, node.innerHTML); + if (node.nodeName == 'TITLE') + document.title = value; + else + node.innerHTML = value; + } +}); diff --git a/Cydia.app/manage.html b/Cydia.app/manage.html new file mode 100644 index 0000000..bba3762 --- /dev/null +++ b/Cydia.app/manage.html @@ -0,0 +1,100 @@ + + + + + + + + + + +
+ +
diff --git a/Cydia.app/manage.png b/Cydia.app/manage.png new file mode 100644 index 0000000..9513a7c Binary files /dev/null and b/Cydia.app/manage.png differ diff --git a/Cydia.app/manage@2x.png b/Cydia.app/manage@2x.png new file mode 100644 index 0000000..cfb8c13 Binary files /dev/null and b/Cydia.app/manage@2x.png differ diff --git a/Cydia.app/menes/chevron.png b/Cydia.app/menes/chevron.png new file mode 100644 index 0000000..6ff8205 Binary files /dev/null and b/Cydia.app/menes/chevron.png differ diff --git a/Cydia.app/menes/chevron@2x.png b/Cydia.app/menes/chevron@2x.png new file mode 100644 index 0000000..9905c17 Binary files /dev/null and b/Cydia.app/menes/chevron@2x.png differ diff --git a/Cydia.app/menes/indicator-c7ced5-4d4d70.gif b/Cydia.app/menes/indicator-c7ced5-4d4d70.gif new file mode 100644 index 0000000..e41bb96 Binary files /dev/null and b/Cydia.app/menes/indicator-c7ced5-4d4d70.gif differ diff --git a/Cydia.app/menes/menes.js b/Cydia.app/menes/menes.js new file mode 100644 index 0000000..dc210bb --- /dev/null +++ b/Cydia.app/menes/menes.js @@ -0,0 +1,575 @@ +/* XXX: this message is ultra-lame */ +var _assert = function (expr, value) { + if (!expr) { + var message = "_assert(" + value + ")"; + console.log(message); + throw message; + } +} + +// Compatibility {{{ +if (typeof Array.prototype.push != "function") + Array.prototype.push = function (value) { + this[this.length] = value; + }; +// }}} + +var $ = function (arg, doc) { + if (this.magic_ != $.prototype.magic_) + return new $(arg); + + if (arg == null) + arg = []; + + var type = $.type(arg); + + if (type == "function") + $.ready(arg); + else if (type == "string") { + if (typeof doc == 'undefined') + doc = document; + if (arg.charAt(0) == '#') { + /* XXX: this is somewhat incorrect-a-porter */ + var element = doc.getElementById(arg.substring(1)); + return $(element == null ? [] : [element]); + } else if (arg.charAt(0) == '.') + return $(doc.getElementsByClassName(arg.substring(1))); + else + return $([doc]).descendants(arg); + } else if (typeof arg.length != 'undefined') { + _assert(typeof doc == 'undefined', "non-query with document to $"); + this.set(arg); + return this; + } else _assert(false, "unknown argument to $: " + typeof arg); +}; + +$.xml = function (value) { + return value + .replace(/&/, "&") + .replace(//, ">") + .replace(/"/, """) + .replace(/'/, "'") + ; +} + +$.type = function (value) { + var type = typeof value; + + if ((type == "function" || type == "object") && value.toString != null) { + var string = value.toString(); + if (string.substring(0, 8) == "[object ") + return string.substring(8, string.length - 1); + } + + return type; +}; + +(function () { + var ready_ = null; + + $.ready = function (_function) { + if (ready_ == null) { + ready_ = []; + + document.addEventListener("DOMContentLoaded", function () { + for (var i = 0; i != ready_.length; ++i) + ready_[i](); + }, false); + } + + ready_.push(_function); + }; +})(); + +/* XXX: verify arg3 overflow */ +$.each = function (values, _function, arg0, arg1, arg2) { + for (var i = 0, e = values.length; i != e; ++i) + _function(values[i], arg0, arg1, arg2); +}; + +/* XXX: verify arg3 overflow */ +$.map = function (values, _function, arg0, arg1, arg2) { + var mapped = []; + for (var i = 0, e = values.length; i != e; ++i) + mapped.push(_function(values[i], arg0, arg1, arg2)); + return mapped; +}; + +$.array = function (values) { + if (values.constructor == Array) + return values; + _assert(typeof values.length != 'undefined', "$.array on underlying non-array"); + var array = []; + for (var i = 0; i != values.length; ++i) + array.push(values[i]); + return array; +}; + +$.document = function (node) { + for (;;) { + var parent = node.parentNode; + if (parent == null) + return node; + node = parent; + } +}; + +$.reclass = function (_class) { + return new RegExp('(\\s|^)' + _class + '(\\s|$)'); +}; + +$.prototype = { + magic_: 2041085062, + + add: function (nodes) { + Array.prototype.push.apply(this, $.array(nodes)); + }, + + at: function (name, value) { + if (typeof value == 'undefined') + return $.map(this, function (node) { + return node.getAttribute(name); + }); + else if (value == null) + $.each(this, function (node) { + node.removeAttribute(); + }); + else + $.each(this, function (node) { + node.setAttribute(name, value); + }); + }, + + set: function (nodes) { + this.length = 0; + this.add(nodes); + }, + + /* XXX: verify arg3 overflow */ + each: function (_function, arg0, arg1, arg2) { + $.each(this, function (node) { + _function($([node]), arg0, arg1, arg2); + }); + }, + + css: function (name, value) { + $.each(this, function (node) { + node.style[name] = value; + }); + }, + + addClass: function (_class) { + $.each(this, function (node) { + if (!$([node]).hasClass(_class)[0]) + node.className += " " + _class; + }); + }, + + blur: function () { + $.each(this, function (node) { + node.blur(); + }); + }, + + focus: function () { + $.each(this, function (node) { + node.focus(); + }); + }, + + removeClass: function (_class) { + $.each(this, function (node) { + node.className = node.className.replace($.reclass(_class), ' '); + }); + }, + + hasClass: function (_class) { + return $.map(this, function (node) { + return node.className.match($.reclass(_class)); + }); + }, + + append: function (children) { + if ($.type(children) == "string") + $.each(this, function (node) { + var doc = $.document(node); + + // XXX: implement wrapper system + var div = doc.createElement("div"); + div.innerHTML = children; + + while (div.childNodes.length != 0) { + var child = div.childNodes[0]; + node.appendChild(child); + } + }); + else + $.each(this, function (node) { + $.each(children, function (child) { + node.appendChild(child); + }); + }); + }, + + xpath: function (expression) { + var value = $([]); + + $.each(this, function (node) { + var doc = $.document(node); + var results = doc.evaluate(expression, node, null, XPathResult.ANY_TYPE, null); + var result; + while (result = results.iterateNext()) + value.add([result]); + }); + + return value; + }, + + clone: function (deep) { + return $($.map(this, function (node) { + return node.cloneNode(deep); + })); + }, + + descendants: function (expression) { + var descendants = $([]); + + $.each(this, function (node) { + var nodes = node.getElementsByTagName(expression); + descendants.add(nodes); + }); + + return descendants; + }, + + remove: function () { + $.each(this, function (node) { + node.parentNode.removeChild(node); + }); + } +}; + +$.scroll = function (x, y) { + window.scrollTo(x, y); +}; + +// XXX: document.all? +$.all = function (doc) { + if (typeof doc == 'undefined') + doc = document; + return $(doc.getElementsByTagName("*")); +}; + +$.inject = function (a, b) { + if ($.type(a) == "string") { + $.prototype[a] = function (value) { + if (typeof value == 'undefined') + return $.map(this, function (node) { + return b.get(node); + }); + else + $.each(this, function (node, value) { + b.set(node, value); + }, value); + }; + } else for (var name in a) + $.inject(name, a[name]); +}; + +$.inject({ + _default: { + get: function (node) { + return node.style.defaultValue; + }, + set: function (node, value) { + node.style.defaultValue = value; + } + }, + + height: { + get: function (node) { + return node.height; + }, + set: function (node, value) { + node.height = value; + } + }, + + html: { + get: function (node) { + return node.innerHTML; + }, + set: function (node, value) { + node.innerHTML = value; + } + }, + + href: { + get: function (node) { + return node.href; + }, + set: function (node, value) { + node.href = value; + } + }, + + name: { + get: function (node) { + return node.name; + }, + set: function (node, value) { + node.name = value; + } + }, + + parent: { + get: function (node) { + return node.parentNode; + } + }, + + src: { + get: function (node) { + return node.src; + }, + set: function (node, value) { + node.src = value; + } + }, + + type: { + get: function (node) { + return node.localName; + } + }, + + value: { + get: function (node) { + return node.value; + }, + set: function (node, value) { + // XXX: do I really need this? + if (true || node.localName != "select") + node.value = value; + else { + var options = node.options; + for (var i = 0, e = options.length; i != e; ++i) + if (options[i].value == value) { + if (node.selectedIndex != i) + node.selectedIndex = i; + break; + } + } + } + }, + + width: { + get: function (node) { + return node.offsetWidth; + } + } +}); + +// Query String Parsing {{{ +$.query = function () { + var args = {}; + + var search = location.search; + if (search != null) { + _assert(search[0] == "?", "query string without ?"); + + var values = search.substring(1).split("&"); + for (var index in values) { + var value = values[index] + var equal = value.indexOf("="); + var name; + + if (equal == -1) { + name = value; + value = null; + } else { + name = value.substring(0, equal); + value = value.substring(equal + 1); + value = decodeURIComponent(value); + } + + name = decodeURIComponent(name); + if (typeof args[name] == "undefined") + args[name] = []; + if (value != null) + args[name].push(value); + } + } + + return args; +}; +// }}} +// Event Registration {{{ +// XXX: unable to remove registration +$.prototype.event = function (event, _function) { + $.each(this, function (node) { + // XXX: smooth over this pointer ugliness + if (node.addEventListener) + node.addEventListener(event, _function, false); + else if (node.attachEvent) + node.attachEvent("on" + event, _function); + else + // XXX: multiple registration SNAFU + node["on" + event] = _function; + }); +}; + +$.each([ + "click", "load", "submit" +], function (event) { + $.prototype[event] = function (_function) { + if (typeof _function == 'undefined') + _assert(false, "undefined function to $.[event]"); + else + this.event(event, _function); + }; +}); +// }}} +// Timed Animation {{{ +$.interpolate = function (duration, event) { + var start = new Date(); + + var next = function () { + setTimeout(update, 0); + }; + + var update = function () { + var time = new Date() - start; + + if (time >= duration) + event(1); + else { + event(time / duration); + next(); + } + }; + + next(); +}; +// }}} +// AJAX Requests {{{ +// XXX: abstract and implement other cases +$.xhr = function (url, method, headers, data, events) { + var xhr = new XMLHttpRequest(); + xhr.open(method, url, true); + + for (var name in headers) + xhr.setRequestHeader(name.replace(/_/, "-"), headers[name]); + + if (events == null) + events = {}; + + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + var status = xhr.status; + var text = xhr.responseText; + if (events.response != null) + events.response(status, text); + if (status == 200) { + if (events.success != null) + events.success(text); + } else { + if (events.failure != null) + events.failure(status); + } + } + }; + + xhr.send(data); +}; + +$.call = function (url, post, onsuccess) { + var events = {}; + + if (onsuccess != null) + events.complete = function (text) { + onsuccess(eval(text)); + }; + + if (post == null) + $.xhr(url, "POST", null, null, events); + else + $.xhr(url, "POST", { + Content_Type: "application/json" + }, $.json(post), events); +}; +// }}} +// WWW Form URL Encoder {{{ +$.form = function (parameters) { + var data = ""; + + var ampersand = false; + for (var name in parameters) { + if (!ampersand) + ampersand = true; + else + data += "&"; + + var value = parameters[name]; + + data += escape(name); + data += "="; + data += escape(value); + } + + return data; +}; +// }}} +// JSON Serializer {{{ +$.json = function (value) { + if (value == null) + return "null"; + + var type = $.type(value); + + if (type == "number") + return value; + else if (type == "string") + return "\"" + value + .replace(/\\/, "\\\\") + .replace(/\t/, "\\t") + .replace(/\r/, "\\r") + .replace(/\n/, "\\n") + .replace(/"/, "\\\"") + + "\""; + else if (value.constructor == Array) { + var json = "["; + var comma = false; + + for (var i = 0; i != value.length; ++i) { + if (!comma) + comma = true; + else + json += ","; + + json += $.json(value[i]); + } + + return json + "]"; + } else if ( + value.constructor == Object && + value.toString() == "[object Object]" + ) { + var json = "{"; + var comma = false; + + for (var name in value) { + if (!comma) + comma = true; + else + json += ","; + + json += name + ":" + $.json(value[name]); + } + return json + "}"; + } else { + return value; + } +}; +// }}} diff --git a/Cydia.app/menes/style.css b/Cydia.app/menes/style.css new file mode 100644 index 0000000..677a2fc --- /dev/null +++ b/Cydia.app/menes/style.css @@ -0,0 +1,885 @@ +/* iPhone.css - iPhone Interface Cascading Style Sheet + * Copyright (C) 2007-2008 Jay Freeman (saurik) +*/ + +/* + * Redistribution and use in source and binary + * forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the + * above copyright notice, this list of conditions + * and the following disclaimer. + * 2. Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions + * and the following disclaimer in the documentation + * and/or other materials provided with the + * distribution. + * 3. The name of the author may not be used to endorse + * or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* .clearfix {{{ */ +.clearfix:after { + content: "."; + display: block; + clear: both; + visibility: hidden; + line-height: 0; + height: 0; +} + +.clearfix { + display: block; +} +/* }}} */ + +* { + border: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; + box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -webkit-box-sizing: border-box; + /*font-family: inherit;*/ + font-size: 100%; + font-style: inherit; + font-weight: inherit; + margin: 0; + outline: 0; + padding: 0; + text-decoration: none; + vertical-align: baseline; +} + +a { + color: inherit; +} + +sup { + font-size: smaller; + margin-top: -6px; + position: relative; + top: -6px; +} + +select { + border: 1px solid #999999; +} + +panel { + display: block; + width: 320px; +} + +body { + font-family: Helvetica, Arial; + -webkit-text-size-adjust: none; + -webkit-user-select: none; + margin: 0 auto; + width: 320px; +} + +body.white { + background-color: #ffffff; +} + +body.pinstripe { + background: #c7ced5 url(cydia://uikit-image/UIPinstripe.png); + background-size: 7px 1px; +} + +dialog { + display: block; + position: absolute; + width: 100%; +} + +dialog > panel { + display: block; +} + +a { + color: blue; + text-underline-style: dotted; +} + +strong { + font-weight: bold +} + +pre, tt { + font-family: monospace; +} + +pre { + letter-spacing: -2px; +} + +em { + font-style: italic; +} + +.default { + color: #aaaabb; +} + +.deleted { + display: none; +} + +/* #toolbar {{{ */ +dialog > toolbar { + background: url(toolbar.png) #6d84a2 repeat-x; + border-bottom: 1px solid #2d3642; + height: 45px; + padding: 10px; +} + +dialog > toolbar > h1 { + color: #ffffff; + font-size: 20px; + font-weight: bold; + height: 100%; + margin: 1px auto 0 auto; + text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0; + text-align: center; + white-space: nowrap; +} +/* }}} */ +/* (back|forward)-button {{{ */ +dialog > toolbar > a.back-button, +dialog > toolbar > a.forward-button { + color: #ffffff; + font-size: 12px; + font-weight: bold; + height: 30px; + line-height: 30px; + margin-top: -28px; + padding: 0 3px; + text-decoration: none; + text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0; + white-space: nowrap; +} + +dialog > toolbar > a.back-button { + -webkit-border-image: url(backButton.png) 0 8 0 14; + border-width: 0 8px 0 14px; + float: left; +} + +dialog > toolbar > a.forward-button { + -webkit-border-image: url(toolButton.png) 0 5 0 5; + border-width: 0 5px; + float: right; +} +/* }}} */ + +panel > block { + /*background-color: #ccd1d5;*/ + /*background-color: white;*/ + /*background-color: #c7ced5;*/ + background-color: transparent; + //background-color: #ffffff; + border-bottom: 1px solid #999999; + border-top: 1px solid #999999; + border-left: 1px dotted #999999; + border-right: 1px dotted #999999; + -moz-border-radius: 9px; + -webkit-border-radius: 9px; + //border: 1px solid #999999; + display: block; + font-size: 16px; + margin: 9px; + padding: 0 10px; +} + +panel > fieldset > div > hr, +panel > block > hr { + border-top: 1px dashed #999999; +} + +panel > fieldset { + background-color: #ffffff; + border: 1px solid #999999; + -moz-border-radius: 9px; + -webkit-border-radius: 9px; + font-size: 16px; + margin: 9px; +} + +panel > input[type="submit"] { + /*-webkit-border-image: url(whiteButton.png) 0 12 0 12; + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-width: 0px 12px;*/ + border: none; + color: #000000; + display: block; + font-size: 20px; + font-weight: bold; + margin: 9px; + height: 44px; + padding: 10px; + text-align: center; + width: 302px; +} + +list > label { + background: #a7b3bc url(cydia://uikit-image/UISectionListHeaderBackground.png); + background-repeat: repeat-x no-repeat-y; + margin-bottom: 0px; + padding: 4px 15px 1px 15px; + display: block; + color: white; + font-size: inherit; + font-weight: bold; + text-shadow: rgba(0, 0, 0, 0.5) 0px 1px 0; +} + +panel > label { + display: block; + margin: 13px 0 -4px 24px; + line-height: 24px; + font-size: inherit; + font-weight: bold; + color: #4d4d70; + text-shadow: rgba(255, 255, 255, 0.75) 1px 1px 0; +} + +panel > fieldset > a, +panel > fieldset > div, +panel > fieldset > textarea { + border-top: 1px solid #999999; +} + +/* XXX: should be a.left:nth-last-child(2) */ +panel > fieldset > a.left, +panel > fieldset > a.middle, +panel > fieldset > a:first-child, +panel > fieldset > div:first-child, +panel > fieldset > textarea:first-child { + border-top: 0; +} + +list > fieldset > a, +list > fieldset > div, +list > fieldset > textarea { + border-bottom: 1px solid #e0e0e0; +} + +fieldset > a:not([type="ad"]), +fieldset > div, +fieldset > textarea { + /* XXX: small differences due to font bugs */ + padding: 12px 14px 10px 14px; +} + +/*fieldset > a:not([type="ad"]):last-child, +fieldset > div:last-child { + padding-bottom: 10px; +}*/ + +fieldset > a[type="ad"] { + /* XXX: small differences due to font bugs */ + padding: 4px 4px 2px 5px; +} + +panel > fieldset > a[type="ad"]:first-child > div:first-child, +panel > fieldset > a[type="comment"]:first-child > div:first-child, +panel > fieldset > a[type="profile"]:first-child > div:first-child, +panel > fieldset > a[type="thumb"]:first-child > div:first-child { + -moz-border-radius-topleft: 9px; + -webkit-border-top-left-radius: 9px; +} + +panel > fieldset > a[type="ad"]:last-child > div:first-child, +panel > fieldset > a[type="comment"]:last-child > div:first-child, +panel > fieldset > a[type="profile"]:last-child > div:first-child, +panel > fieldset > a[type="thumb"]:last-child > div:first-child { + -moz-border-radius-bottomleft: 9px; + -webkit-border-bottom-left-radius: 9px; +} + +fieldset > a[type="ad"] > div:first-child { + border: 1px solid #999999; +} + +list > fieldset > a[type="comment"] > div:first-child, +list > fieldset > a[type="profile"] > div:first-child, +list > fieldset > a[type="thumb"] > div:first-child { + border: 1px solid #e0e0e0; +} + +panel > fieldset > a[type="comment"] > div:first-child { + border: 1px solid #999999; + border-bottom-style: dashed; +} + +panel > fieldset > a[type="profile"] > div:first-child, +panel > fieldset:not(.header) > a[type="thumb"] > div:first-child { + border: 1px solid #999999; +} + +div[tile] { + float: right; + height: 30px; + width: 30px; +} + +div[tile="app"] { background-image: url(http://cache.saurik.com/cydia/tile/app.png); } +div[tile="call"] { background-image: url(http://cache.saurik.com/cydia/tile/call.png); } +div[tile="map"] { background-image: url(http://cache.saurik.com/cydia/tile/map.png); } +div[tile="media"] { background-image: url(http://cache.saurik.com/cydia/tile/media.png); } +div[tile="music"] { background-image: url(http://cache.saurik.com/cydia/tile/video.png); } +div[tile="site"] { background-image: url(http://cache.saurik.com/cydia/tile/site.png); } + +fieldset > a[type="ad"] > div:first-child { + background-repeat: no-repeat; + background-position: center center; + border-right: none; + display: inline-block; + height: 40px; + line-height: 38px; + /* XXX: small differences due to font bugs */ + /* XXX: 1px difference due to border stupidity */ + margin: -5px 5px -3px -6px; + width: 40px; +} + +panel > fieldset > a[type="comment"] { + border-bottom-style: dashed; +} + +fieldset > a[type="comment"] > div:first-child { + -webkit-background-size: 44px; + height: 44px; + width: 44px; +} + +fieldset > a[type="header"] > div:first-child { + height: 64px; + width: 64px; +} + +fieldset > a[type="profile"] > div:first-child { + -webkit-background-size: 50px; + height: 50px; + width: 50px; +} + +fieldset > a[type="thumb"] > div:first-child { + height: 64px; + width: 64px; +} + +fieldset > a[type="comment"] > div:first-child, +fieldset > a[type="header"] > div:first-child, +fieldset > a[type="profile"] > div:first-child, +fieldset > a[type="thumb"] > div:first-child { + background-repeat: no-repeat; + background-position: center center; + display: inline-block; + /* XXX: small differences due to font bugs */ + /* XXX: 1px difference due to border stupidity */ + margin: -13px 7px -11px -15px; +} + +fieldset > a > img.icon, +fieldset > div > img.icon { + height: auto; + /* XXX: small differences due to font bugs */ + margin: -7px 6px -9px -8px; + max-height: 30px; + min-width: 30px; + width: 30px; +} + +fieldset > a.sixth > img.icon { + position: relative; + left: 7px; +} + +panel > block > p, +fieldset > div > p, +panel > block > ul, +fieldset > div > ul { + margin: 10px 0; +} + +panel > block > ul, +fieldset > div > ul { + margin-left: 13px; +} + +panel > block > p, +fieldset > div > p { + text-align: center; +} + +fieldset > div > p:first-child, +fieldset > div > ul:first-child { + margin-top: 0; +} + +fieldset > div > p:last-child, +fieldset > div > ul:last-child { + margin-bottom: 0; +} + +fieldset > a { + color: inherit; + display: block; +} + +fieldset > textarea, +fieldset > div > input:not([type="checkbox"]), +fieldset > div > select, +fieldset > div > div > select { + background: none; + -webkit-box-shadow: none; + -webkit-appearance: none; +} + +/* Chevrons {{{ */ + +fieldset > a[href]:not([type="ad"]), +fieldset > div > select, +fieldset > div > div > select { + background-repeat: no-repeat; + background-image: url(chevron.png); +} + +@media screen and (-webkit-min-device-pixel-ratio: 2) { +fieldset > a[href]:not([type="ad"]), +fieldset > div > select, +fieldset > div > div > select { + background-image: url(chevron@2x.png); + background-size: 10px 13px; +} } + +/* Horizontal */ + +list > fieldset > a[href] { + background-position: 295px center; +} + +panel > fieldset > a[href] { + background-position: 275px center; +} + +panel > fieldset > a[href].half { + background-position: 125px center; +} + +panel > fieldset > a[href].third { + background-position: 75px center; +} + +panel > fieldset > a[href].sixth { + background: none; +} + +list > fieldset > a:not([href]) > select, +list > fieldset > div > select, +list > fieldset > div > div > select { + background-position: 183px center; +} + +panel > fieldset > a:not([href]) > select, +panel > fieldset > div > select, +panel > fieldset > div > div > select { + background-position: 163px center; +} + +/* }}} */ + +fieldset > textarea, +fieldset > div > input, +fieldset > div > select, +fieldset > div > div > select, +fieldset > a > div > label + label, +fieldset > div > div > label + label { + color: #193250; +} + +fieldset > textarea, +fieldset > div > input, +fieldset > div > select, +fieldset > div > div > select { + font-size: 16px; +} + +fieldset > div > input { + padding-left: 7px; + padding-right: 14px; +} + +fieldset > div > input[type="checkbox"] { + border: 1px solid #999999; + -moz-border-radius: 7px; + -webkit-border-radius: 7px; + float: right; + margin: -7px -8px; + height: 30px; + width: 30px; +} + +fieldset > div > select, +fieldset > div > div > select, +fieldset > div > input:not([type="checkbox"]) { + border: none; + float: right; + height: 40px; + margin: -11px -13px -11px -14px; +} + +panel > fieldset > div > select, +panel > fieldset > div > div > select, +panel > fieldset > div > input:not([type="checkbox"]) { + width: 187px; +} + +list > fieldset > div > select, +list > fieldset > div > div > select, +list > fieldset > div > input:not([type="checkbox"]) { + width: 207px; +} + +fieldset > textarea { + padding: 10px; + width: 320px; +} + +fieldset > div > div, +fieldset > a > div { + display: inline-block; +} + +fieldset > div > div { + width: 273px; +} + +fieldset > a[type="ad"] > div:nth-child(2) { + width: 218px; +} + +fieldset > a:not([type]) > div { + width: 250px; +} + +fieldset > a:not([href]) > img.icon + div, +fieldset > div > img.icon + div { + width: 244px; +} + +fieldset > a[href] > img.icon + div { + width: 221px; +} + +fieldset > a[type="profile"] > div:nth-child(2) > label:nth-child(1).unknown { + color: #aaaabb; +} + +fieldset > a[type="profile"] > div:nth-child(2) > label:only-child { + left: 4px; + position: relative; + top: 3px; +} + +fieldset > a[type="thumb"] > div:nth-child(2) > label:only-child { + position: relative; + top: 10px; +} + +fieldset > a[type="profile"] > div:nth-child(2) > label + label { + display: block; + font-size: 13px; + margin-top: 2px; +} + +fieldset > a[type="thumb"] > div:nth-child(2) > label + label { + display: block; + margin-top: 2px; +} + +fieldset > a[type="profile"] > div:nth-child(2) { + width: 207px; +} + +fieldset > a[type="thumb"] > div:nth-child(2) { + width: 193px; +} + +fieldset > a[type="profile"] > div:nth-child(2) { + margin: -5px 0; +} + +fieldset > a[type="profile"] > div:nth-child(2), +fieldset > a[type="thumb"] > div:nth-child(2) { + vertical-align: top; +} + +fieldset > a > label:first-child, +fieldset > a > div > label:first-child, +fieldset > div > label:first-child, +fieldset > div > div > label:first-child { + font-weight: bold; +} + +/* XXX: this doesn't handle icon offsets */ +list > fieldset > a:not([type]) > div > label + label, +list > fieldset > div > div > label + label { + margin-left: 94px; +} + +panel > fieldset > a:not([type]) > div > label + label, +panel > fieldset > div > div > label + label { + float: right; + text-align: right; +} + +panel > img { + display: block; + margin: 9px auto 4px auto; + height: auto; + width: 300px; +} + +fieldset > a[type="ad"] { +} + +fieldset > a[type="ad"] > div:nth-child(2) { + position: relative; + vertical-align: top; +} + +fieldset > a[type="ad"] > div > label:first-child { + color: #2d2d50; + font-size: 13px; + font-weight: bold; + line-height: 15px; +} + +fieldset > a[type="ad"] > div > label + label { + position: absolute; + top: 17px; + left: 156px; + font-size: 9.5px; + font-weight: normal; +} + +panel > fieldset > a.middle, +panel > fieldset > a.right { + border-left: 1px solid #999999; +} + +panel > fieldset > a.half { + display: inline-block; + width: 150px; +} + +panel > fieldset > a.third { + display: inline-block; + width: 100px; +} + +panel > fieldset > a.sixth { + display: inline-block; + width: 50px; +} + +fieldset.half > a { + background: none; + background-position: 120px center; +} + +fieldset.half > a > img.icon + div { + width: 65px; +} + +fieldset.right { + float: right; + margin-left: 10px; +} + +block + fieldset.right, +fieldset + fieldset.right { + margin-top: 0; +} + +fieldset.half { + width: 146px; +} + +panel > fieldset.dashed > a, +panel > fieldset.dashed > div, +panel > fieldset.dashed > textarea, +list > fieldset.dashed > a, +list > fieldset.dashed > div, +list > fieldset.dashed > textarea { + border-style: dashed; +} + +fieldset > a[type="thumb"]:first-child > back { + -moz-border-radius-topright: 9px; + -webkit-border-top-right-radius: 9px; +} + +fieldset > a[type="thumb"]:last-child > back { + -moz-border-radius-bottomright: 9px; + -webkit-border-bottom-right-radius: 9px; +} + +fieldset > a[type="thumb"] > back { + background-repeat: no-repeat; + border: 1px solid #999999; + display: block; + height: 64px; + left: 62px; + position: absolute; + opacity: 0.2; + top: -1px; + width: 237px; +} + +.mm { + border: 1px solid #999999; + -moz-border-radius: 9px; + -webkit-border-radius: 9px; +} + +a.mm { + display: block; + margin: 9px; +} + +div.mm img { + height: auto; + width: 300px; +} + +fieldset > a.small { + font-size: 12px; + padding-top: 9px; +} + +fieldset > a.small label { + display: inline-block; + position: relative; + top: 1px; +} + +fieldset > a.small > img.icon { + max-height: 22px; + min-width: 22px; + width: 22px; +} + +fieldset > a.small.half > img.icon + div { + width: 79px; +} + +fieldset > a.small.third > img.icon + div { + width: 50px; +} + +fieldset > a.small.sixth > img.icon + div { + width: 0px; +} + +panel.centered > label { + margin-left: 0px; + margin-right: 0px; + text-align: center; +} + +panel > iframe { + margin: -9px 0; +} + +panel > iframe:first-child, +panel > iframe + iframe { + margin-top: 0; +} + +/* Rating Stars {{{ */ +.ratings { + margin: -2px 0; + text-align: center; +} + +.rated { + display: inline-block; +} + +.rated.left { + margin-right: 9px; +} + +.rated label { + font-weight: bold; + margin-right: 3px; + position: relative; + top: -3px; +} + +.rating { + display: inline-block; + width: 80px; +} + +.rating .back, +.rating .fore, +.rating .star { + background: url(http://cache.saurik.com/crystal/16x16/actions/knewstuff.png); + height: 16px; +} + +.rating .back, +.rating .fore { + width: 80px; +} + +.rating .star { + display: inline-block; + width: 16px; +} + +.rating .back { + opacity: 0.2; +} + +.rating .fore { + /*border-right: 1px solid #999999;*/ + position: absolute; +} +/* }}} */ + +panel > fieldset.header { + background-color: transparent; + border: none; + margin: -5px 9px -11px 9px; +} + +panel > fieldset.header > a > div > label { + color: #4d4d70; + text-shadow: rgba(255, 255, 255, 0.75) 1px 1px 0; +} diff --git a/Cydia.app/package.html b/Cydia.app/package.html new file mode 100644 index 0000000..aacd6b7 --- /dev/null +++ b/Cydia.app/package.html @@ -0,0 +1,384 @@ + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+ +
+ +
+ + +
+ + +
+ + + + +
+ +
+ + +
+ +
+
+ +
+
+
+ +
+
+ + + + + + + + + + + +

+ +
+ +
+ +
+
+ + +
+
+
+ + +
+ + +
+ +
+
+ + +
+
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ + +
+ + +
+ + + + + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+ +
+ diff --git a/Cydia.app/package.js b/Cydia.app/package.js new file mode 100644 index 0000000..6e091cf --- /dev/null +++ b/Cydia.app/package.js @@ -0,0 +1,326 @@ +/*var package = { + "name": "MobileTerminal", + "latest": "286u-5", + "author": { + "name": "Allen Porter", + "address": "allen.porter@gmail.com" + }, + //"depiction": "http://planet-iphones.com/repository/info/chromium1.3.php", + "depiction": "http://cydia.saurik.com/terminal.html", + "longDescription": "this is a sample description", + "homepage": "http://cydia.saurik.com/terminal.html", + "installed": "286u-4", + "id": "mobileterminal", + "section": "Terminal Support", + "size": 552*1024, + "maintainer": { + "name": "Jay Freeman", + "address": "saurik@saurik.com" + }, + "source": { + "name": "Telesphoreo Tangelo", + "description": "Distribution of Unix Software for the iPhone" + } +};*/ + +function space(selector, html, max) { + var node = $(selector); + node.html(html); + var width = node.width(); + if (width > max) { + var spacing = (max - node.width()) / (html.length - 1) + "px"; + node.css("letter-spacing", spacing); + } +} + +var swap_, swap = function (on, off, time) { + setTimeout(swap_(on, off, time), time); +}; + +swap_ = function (on, off, time) { + return function () { + on.className = 'fade-out'; + off.className = 'fade-in'; + swap(off, on, time); + }; +}; + +var special_ = function () { + if (package == null) + return; + + var id = package.id; + var idc = encodeURIComponent(id); + var name = package.name; + var icon = 'cydia://package-icon/' + idc; + + var api = 'http://cydia.saurik.com/api/'; + var capi = 'http://cache.cydia.saurik.com/api/'; + + var support = package.support; + + var regarding = function (type) { + return encodeURIComponent("Cydia/APT(" + type + "): " + name); + }; + + $("#icon").css("background-image", 'url("' + icon + '")'); + //$("#reflection").src("cydia://package-icon/" + idc); + + $("#name").html(name); + space("#latest", package.latest, 96); + + $.xhr(capi + 'package/' + idc, 'GET', {}, null, { + success: function (value) { + value = eval(value); + + if (typeof value.notice == "undefined") + $(".notice").addClass("deleted"); + else + $("#notice-src").src(value.notice); + + if (typeof value.rating == "undefined") + $(".rating").addClass("deleted"); + else { + $("#rating-load").addClass("deleted"); + $("#rating-href").href(value.reviews); + + var none = $("#rating-none"); + var done = $("#rating-done"); + + if (value.rating == null) { + none.css("display", "block"); + } else { + done.css("display", "block"); + + $("#rating-value").css('width', 16 * value.rating); + } + } + + if (typeof value.icon != "undefined" && value.icon != null) { + var icon = $("#icon"); + var thumb = $("#thumb"); + + icon[0].className = 'flip-180'; + thumb[0].className = 'flip-360'; + + thumb.css("background-image", 'url("' + value.icon + '")'); + + setTimeout(function () { + icon.addClass("deleted"); + thumb[0].className = 'flip-0'; + }, 2000); + } + }, + + failure: function (status) { + $(".rating").addClass("deleted"); + } + }); + + $("#settings").href("cydia://package-settings/" + idc); + + var mode = package.mode; + if (mode == null) + $(".mode").addClass("deleted"); + else { + $("#mode").html(cydia.localize(mode)); + $("#mode-src").src("Modes/" + mode + ".png"); + } + + var warnings = package.warnings; + var length = warnings == null ? 0 : warnings.length; + if (length == 0) + $(".warnings").addClass("deleted"); + else { + var parent = $("#warnings"); + var child = $("#warning"); + + for (var i = 0; i != length; ++i) { + var clone = child.clone(true); + clone.addClass("inserted"); + parent.append(clone); + clone.xpath("./div/label").html($.xml(warnings[i])); + } + + child.addClass("deleted"); + } + + var applications = package.applications; + var length = applications == null ? 0 : applications.length; + + var child = $("#application"); + + /*if (length != 0) { + var parent = $("#actions"); + + for (var i = 0; i != length; ++i) { + var application = applications[i]; + var clone = child.clone(true); + parent.append(clone); + clone.href("cydia://launch/" + application[0]); + clone.xpath("label").html("Run " + $.xml(application[1])); + clone.xpath("img").src(application[2]); + } + }*/ + + child.addClass("deleted"); + + var commercial = package.hasTag('cydia::commercial'); + if (!commercial) + $(".commercial").addClass("deleted"); + + var _console = package.hasTag('purpose::console'); + if (!_console) + $(".console").addClass("deleted"); + + var author = package.author; + if (author == null) + $(".author").addClass("deleted"); + else { + space("#author", author.name, 160); + if (author.address == null) + $("#author-icon").addClass("deleted"); + else if (support == null) + $("#author-href").href("mailto:" + author.address + "?subject=" + regarding("A")); + else + $("#author-href").href(support); + } + + /*var store = commercial; + if (!store) + $(".activation").addClass("deleted"); + else { + var activation = api + 'activation/' + idc; + $("#activation-src").src(activation); + }*/ + + var depiction = package.depiction; + if (depiction == null) + $(".depiction").addClass("deleted"); + else { + $(".description").addClass("deleted"); + $("#depiction-src").src(depiction); + } + + var description = package.longDescription; + if (description == null) + description = package.shortDescription; + else + description = description.replace(/\n/g, "
"); + $("#description").html(description); + + var homepage = package.homepage; + if (homepage == null) + $(".homepage").addClass("deleted"); + else + $("#homepage-href").href(homepage); + + var installed = package.installed; + if (installed == null) + $(".installed").addClass("deleted"); + else { + $("#installed").html(installed); + $("#files-href").href("cydia://files/" + idc); + } + + space("#id", id, 220); + + var section = package.longSection; + if (section == null) + $(".section").addClass("deleted"); + else { + $("#section-src").src("cydia://section-icon/" + encodeURIComponent(section)); + $("#section").html(section); + } + + var size = package.size; + if (size == 0) + $(".size").addClass("deleted"); + else + $("#size").html(size / 1024 + " kB"); + + var maintainer = package.maintainer; + if (maintainer == null) + $(".maintainer").addClass("deleted"); + else { + space("#maintainer", maintainer.name, 153); + if (maintainer.address == null) + $("#maintainer-icon").addClass("deleted"); + else if (support == null) + $("#maintainer-href").href("mailto:" + maintainer.address + "?subject=" + regarding("M")); + else + $("#maintainer-href").href(support); + } + + var sponsor = package.sponsor; + if (sponsor == null) + $(".sponsor").addClass("deleted"); + else { + space("#sponsor", sponsor.name, 152); + $("#sponsor-href").href(sponsor.address); + } + + var source = package.source; + if (source == null) { + $(".source").addClass("deleted"); + $(".trusted").addClass("deleted"); + } else { + var host = source.host; + + $("#source-src").src("cydia://source-icon/" + encodeURIComponent(host)); + $("#source-name").html(source.name); + + if (source.trusted) + $("#trusted").href("cydia://package-signature/" + idc); + else + $(".trusted").addClass("deleted"); + + var description = source.description; + if (description == null) + $(".source-description").addClass("deleted"); + else + $("#source-description").html(description); + } +}; + +$(special_); + +var special = function () { + $(".deleted").removeClass("deleted"); + $(".inserted").remove(); + + $("#icon")[0].className = 'flip-0'; + $("#thumb")[0].className = 'flip-180'; + + /* XXX: this could be better */ + $("#rating-href").href(null); + $("#rating-none").css("display", "none"); + $("#rating-done").css("display", "none"); + + var notice = $("#notice-src"); + + notice[0].outerHTML = ''; + + var depiction = $("#depiction-src"); + + depiction[0].outerHTML = ''; + + special_(); +}; + +cydia.setSpecial(special); diff --git a/Cydia.app/packages.png b/Cydia.app/packages.png new file mode 100644 index 0000000..404fe0c Binary files /dev/null and b/Cydia.app/packages.png differ diff --git a/Cydia.app/reload.png b/Cydia.app/reload.png new file mode 100644 index 0000000..8d06eb4 Binary files /dev/null and b/Cydia.app/reload.png differ diff --git a/Cydia.app/removing.png b/Cydia.app/removing.png new file mode 100644 index 0000000..935d4c7 Binary files /dev/null and b/Cydia.app/removing.png differ diff --git a/Cydia.app/search.png b/Cydia.app/search.png new file mode 100644 index 0000000..4083f1b Binary files /dev/null and b/Cydia.app/search.png differ diff --git a/Cydia.app/search@2x.png b/Cydia.app/search@2x.png new file mode 100644 index 0000000..74d6381 Binary files /dev/null and b/Cydia.app/search@2x.png differ diff --git a/Cydia.app/settings.png b/Cydia.app/settings.png new file mode 100644 index 0000000..86f4e78 Binary files /dev/null and b/Cydia.app/settings.png differ diff --git a/Cydia.app/signature.html b/Cydia.app/signature.html new file mode 100644 index 0000000..aa113b8 --- /dev/null +++ b/Cydia.app/signature.html @@ -0,0 +1,16 @@ + + + Signature + + + + + + +
+
+
+ +
+
+
diff --git a/Cydia.app/sources.png b/Cydia.app/sources.png new file mode 100644 index 0000000..e8422e4 Binary files /dev/null and b/Cydia.app/sources.png differ diff --git a/Cydia.app/star.png b/Cydia.app/star.png new file mode 100644 index 0000000..f762a24 Binary files /dev/null and b/Cydia.app/star.png differ diff --git a/Cydia.app/storage.html b/Cydia.app/storage.html new file mode 100644 index 0000000..55ac5ab --- /dev/null +++ b/Cydia.app/storage.html @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ + +
A small partition used to store iPhone OS. Cydia adds a few important programs and libraries.
+ +
+
+ + +
+
+
+
+
+
+
+
+
+
+ + +
Most content is stored on this partition: from applications (Cydia and Apple) to multimedia.
+ +
+
+ +
+ diff --git a/Cydia.app/storage.js b/Cydia.app/storage.js new file mode 100644 index 0000000..ae8cb85 --- /dev/null +++ b/Cydia.app/storage.js @@ -0,0 +1,88 @@ +var colors = ["#9090e0", "#4d4d70", "#7d7da0", "#7da0e0", "#d0d0f0", "#7070e0"]; + +var list = function (legend, color, name, value) { + legend.append('
' + + '
' + + '
' + name + ' (' + Math.round(value * 1000) / 10 + '%)
' + + '
'); +}; + +console.log(cydia.statfs("/")); + +var cut = function (parent, color, fraction, z) { + var deg = Math.round(360 * fraction); + if (deg < 2) + deg = 2; + parent.append('
'); +}; + +var chart = function (right, left, slices) { + var total = 0; + for (var i = 0; i != slices.length; ++i) { + var slice = slices[i]; + var z = slices.length - i; + if (slice[1] > 0.5) + cut(right, slice[0], total + 0.5, z); + total += slice[1]; + cut(total > 0.5 ? left : right, slice[0], total, z); + } +}; + +var setup = function (name, root, folders) { + var size = $("#" + name + "-size"); + var statfs = cydia.statfs(root); + var kb = statfs[0] * statfs[1] / 1024; + var total = kb / 1024; + + var unit; + if (total < 1000) + unit = 'M'; + else { + total = total / 1024; + unit = 'G' + } + + size.html(Math.round(total * 10) / 10 + " " + unit); + + var legend = $("#" + name + "-legend"); + var used = 0; + + var slices = []; + + if (folders != null) + for (var i = 0; i != folders.length; ++i) { + var folder = folders[i]; + var usage = cydia.du(folder[1]); + if (usage == null) + usage = 0; + var color = colors[i + 2]; + var percent = usage / kb; + list(legend, color, folder[0], percent); + slices.push([color, percent]); + used += usage; + } + + var free = statfs[0] * statfs[2] / 1024; + var other = (kb - free - used) / kb; + + slices.push([colors[0], other]); + chart($("#" + name + "-right"), $("#" + name + "-left"), slices); + + list(legend, colors[0], folders == null ? "Used" : "Other", other); + list(legend, colors[1], "Free", statfs[2] / statfs[1]); +}; + +$(function () { + setup("system", "/", null); + + setup("private", "/private/var", [ + ["Themes", "/Library/Themes/"], + ["iTunes", "/var/mobile/Media/iTunes_Control/"], + ["App Store", "/var/mobile/Applications/"], + ["Camera", "/var/mobile/Media/DCIM/"] + ]); +}); diff --git a/Cydia.app/storage.png b/Cydia.app/storage.png new file mode 100644 index 0000000..3cefef0 Binary files /dev/null and b/Cydia.app/storage.png differ diff --git a/Cydia.app/trusted.png b/Cydia.app/trusted.png new file mode 100644 index 0000000..de249eb Binary files /dev/null and b/Cydia.app/trusted.png differ diff --git a/Cydia.app/unknown.html b/Cydia.app/unknown.html new file mode 100644 index 0000000..900a86f --- /dev/null +++ b/Cydia.app/unknown.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + +
+

:

+
+ + +

+

+
+ +
+ diff --git a/Cydia.app/unknown.png b/Cydia.app/unknown.png new file mode 100644 index 0000000..92d7338 Binary files /dev/null and b/Cydia.app/unknown.png differ diff --git a/Cydia.app/version.png b/Cydia.app/version.png new file mode 100644 index 0000000..fd1eec1 Binary files /dev/null and b/Cydia.app/version.png differ diff --git a/Cydia.app/warning.png b/Cydia.app/warning.png new file mode 100644 index 0000000..8d3e5df Binary files /dev/null and b/Cydia.app/warning.png differ diff --git a/Cydia.app/web.png b/Cydia.app/web.png new file mode 100644 index 0000000..a4f04e3 Binary files /dev/null and b/Cydia.app/web.png differ diff --git a/Cydia.mm b/Cydia.mm new file mode 100644 index 0000000..8d93381 --- /dev/null +++ b/Cydia.mm @@ -0,0 +1,8774 @@ +/* Cydia - iPhone UIKit Front-End for Debian APT + * Copyright (C) 2008-2010 Jay Freeman (saurik) +*/ + +/* Modified BSD License {{{ */ +/* + * Redistribution and use in source and binary + * forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the + * above copyright notice, this list of conditions + * and the following disclaimer. + * 2. Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions + * and the following disclaimer in the documentation + * and/or other materials provided with the + * distribution. + * 3. The name of the author may not be used to endorse + * or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* }}} */ + +// XXX: wtf/FastMalloc.h... wtf? +#define USE_SYSTEM_MALLOC 1 + +/* #include Directives {{{ */ +#import "UICaboodle/UCPlatform.h" +#import "UICaboodle/UCLocalize.h" + +#include +#include + +#include +#include +#include + +#if 0 +#define DEPLOYMENT_TARGET_MACOSX 1 +#define CF_BUILDING_CF 1 +#include +#endif + +#include +#include + +#import + +#include +#import + +#include +#include +#include +#include + +#include + +#undef ABS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include +} + +#include +#include +#include + +#include +#include + +#include + +#import "UICaboodle/BrowserView.h" +#import "UICaboodle/ResetView.h" + +#import "substrate.h" +/* }}} */ + +/* Profiler {{{ */ +struct timeval _ltv; +bool _itv; + +#define _timestamp ({ \ + struct timeval tv; \ + gettimeofday(&tv, NULL); \ + tv.tv_sec * 1000000 + tv.tv_usec; \ +}) + +typedef std::vector TimeList; +TimeList times_; + +class ProfileTime { + private: + const char *name_; + uint64_t total_; + uint64_t count_; + + public: + ProfileTime(const char *name) : + name_(name), + total_(0) + { + times_.push_back(this); + } + + void AddTime(uint64_t time) { + total_ += time; + ++count_; + } + + void Print() { + if (total_ != 0) + std::cerr << std::setw(5) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl; + total_ = 0; + count_ = 0; + } +}; + +class ProfileTimer { + private: + ProfileTime &time_; + uint64_t start_; + + public: + ProfileTimer(ProfileTime &time) : + time_(time), + start_(_timestamp) + { + } + + ~ProfileTimer() { + time_.AddTime(_timestamp - start_); + } +}; + +void PrintTimes() { + for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i) + (*i)->Print(); + std::cerr << "========" << std::endl; +} + +#define _profile(name) { \ + static ProfileTime name(#name); \ + ProfileTimer _ ## name(name); + +#define _end } +/* }}} */ + +#define _pooled _H _pool([[NSAutoreleasePool alloc] init], true); + +static const NSUInteger UIViewAutoresizingFlexibleBoth(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + +void NSLogPoint(const char *fix, const CGPoint &point) { + NSLog(@"%s(%g,%g)", fix, point.x, point.y); +} + +void NSLogRect(const char *fix, const CGRect &rect) { + NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); +} + +static _finline NSString *CydiaURL(NSString *path) { + char page[25]; + page[0] = 'h'; page[1] = 't'; page[2] = 't'; page[3] = 'p'; page[4] = ':'; + page[5] = '/'; page[6] = '/'; page[7] = 'c'; page[8] = 'y'; page[9] = 'd'; + page[10] = 'i'; page[11] = 'a'; page[12] = '.'; page[13] = 's'; page[14] = 'a'; + page[15] = 'u'; page[16] = 'r'; page[17] = 'i'; page[18] = 'k'; page[19] = '.'; + page[20] = 'c'; page[21] = 'o'; page[22] = 'm'; page[23] = '/'; page[24] = '\0'; + return [[NSString stringWithUTF8String:page] stringByAppendingString:path]; +} + +/* [NSObject yieldToSelector:(withObject:)] {{{*/ +@interface NSObject (Cydia) +- (id) yieldToSelector:(SEL)selector withObject:(id)object; +- (id) yieldToSelector:(SEL)selector; +@end + +@implementation NSObject (Cydia) + +- (void) doNothing { +} + +- (void) _yieldToContext:(NSMutableArray *)context { _pooled + SEL selector(reinterpret_cast([[context objectAtIndex:0] pointerValue])); + id object([[context objectAtIndex:1] nonretainedObjectValue]); + volatile bool &stopped(*reinterpret_cast([[context objectAtIndex:2] pointerValue])); + + /* XXX: deal with exceptions */ + id value([self performSelector:selector withObject:object]); + + NSMethodSignature *signature([self methodSignatureForSelector:selector]); + [context removeAllObjects]; + if ([signature methodReturnLength] != 0 && value != nil) + [context addObject:value]; + + stopped = true; + + [self + performSelectorOnMainThread:@selector(doNothing) + withObject:nil + waitUntilDone:NO + ]; +} + +- (id) yieldToSelector:(SEL)selector withObject:(id)object { + /*return [self performSelector:selector withObject:object];*/ + + volatile bool stopped(false); + + NSMutableArray *context([NSMutableArray arrayWithObjects: + [NSValue valueWithPointer:selector], + [NSValue valueWithNonretainedObject:object], + [NSValue valueWithPointer:const_cast(&stopped)], + nil]); + + NSThread *thread([[[NSThread alloc] + initWithTarget:self + selector:@selector(_yieldToContext:) + object:context + ] autorelease]); + + [thread start]; + + NSRunLoop *loop([NSRunLoop currentRunLoop]); + NSDate *future([NSDate distantFuture]); + + while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); + + return [context count] == 0 ? nil : [context objectAtIndex:0]; +} + +- (id) yieldToSelector:(SEL)selector { + return [self yieldToSelector:selector withObject:nil]; +} + +@end +/* }}} */ + +@interface CYActionSheet : UIActionSheet { + unsigned button_; +} + +- (int) yieldToPopupAlertAnimated:(BOOL)animated; +@end + +@implementation CYActionSheet + +- (id) initWithTitle:(NSString *)title buttons:(NSArray *)buttons defaultButtonIndex:(int)index { + if ((self = [super initWithTitle:title buttons:buttons defaultButtonIndex:index delegate:self context:nil]) != nil) { + } return self; +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + button_ = button; +} + +- (int) yieldToPopupAlertAnimated:(BOOL)animated { + button_ = 0; + [self popupAlertAnimated:animated]; + NSRunLoop *loop([NSRunLoop currentRunLoop]); + NSDate *future([NSDate distantFuture]); + while (button_ == 0 && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); + return button_; +} + +@end + +/* NSForcedOrderingSearch doesn't work on the iPhone */ +static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch; +static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch; +static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareLocalized | kCFCompareNumerically | kCFCompareWidthInsensitive | kCFCompareForcedOrdering; + +/* Information Dictionaries {{{ */ +@interface NSMutableArray (Cydia) +- (void) addInfoDictionary:(NSDictionary *)info; +@end + +@implementation NSMutableArray (Cydia) + +- (void) addInfoDictionary:(NSDictionary *)info { + [self addObject:info]; +} + +@end + +@interface NSMutableDictionary (Cydia) +- (void) addInfoDictionary:(NSDictionary *)info; +@end + +@implementation NSMutableDictionary (Cydia) + +- (void) addInfoDictionary:(NSDictionary *)info { + [self setObject:info forKey:[info objectForKey:@"CFBundleIdentifier"]]; +} + +@end +/* }}} */ +/* Pop Transitions {{{ */ +@interface PopTransitionView : UITransitionView { +} + +@end + +@implementation PopTransitionView + +- (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to { + if (from != nil && to == nil) + [self removeFromSuperview]; +} + +@end + +@implementation UIView (PopUpView) + +- (void) popFromSuperviewAnimated:(BOOL)animated { + [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil]; +} + +- (void) popSubview:(UIView *)view { + UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]); + [transition setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [self addSubview:transition]; + + [transition setDelegate:transition]; + + UIView *blank([[[UIView alloc] initWithFrame:[transition bounds]] autorelease]); + [blank setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [transition transition:UITransitionNone toView:blank]; + [transition transition:UITransitionPushFromBottom toView:view]; +} + +@end +/* }}} */ + +#define lprintf(args...) fprintf(stderr, args) + +#define ForRelease 0 +#define TraceLogging (1 && !ForRelease) +#define HistogramInsertionSort (0 && !ForRelease) +#define ProfileTimes (0 && !ForRelease) +#define ForSaurik (0 && !ForRelease) +#define LogBrowser (0 && !ForRelease) +#define TrackResize (0 && !ForRelease) +#define ManualRefresh (0 && !ForRelease) +#define ShowInternals (0 && !ForRelease) +#define IgnoreInstall (0 && !ForRelease) +#define RecycleWebViews 0 +#define RecyclePackageViews (1 && ForRelease) +#define AlwaysReload (1 && !ForRelease) + +#if !TraceLogging +#undef _trace +#define _trace(args...) +#endif + +#if !ProfileTimes +#undef _profile +#define _profile(name) { +#undef _end +#define _end } +#define PrintTimes() do {} while (false) +#endif + +/* Radix Sort {{{ */ +typedef uint32_t (*SKRadixFunction)(id, void *); + +@interface NSMutableArray (Radix) +- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object; +- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument; +@end + +struct RadixItem_ { + size_t index; + uint32_t key; +}; + +static void RadixSort_(NSMutableArray *self, size_t count, struct RadixItem_ *swap) { + struct RadixItem_ *lhs(swap), *rhs(swap + count); + + static const size_t width = 32; + static const size_t bits = 11; + static const size_t slots = 1 << bits; + static const size_t passes = (width + (bits - 1)) / bits; + + size_t *hist(new size_t[slots]); + + for (size_t pass(0); pass != passes; ++pass) { + memset(hist, 0, sizeof(size_t) * slots); + + for (size_t i(0); i != count; ++i) { + uint32_t key(lhs[i].key); + key >>= pass * bits; + key &= _not(uint32_t) >> width - bits; + ++hist[key]; + } + + size_t offset(0); + for (size_t i(0); i != slots; ++i) { + size_t local(offset); + offset += hist[i]; + hist[i] = local; + } + + for (size_t i(0); i != count; ++i) { + uint32_t key(lhs[i].key); + key >>= pass * bits; + key &= _not(uint32_t) >> width - bits; + rhs[hist[key]++] = lhs[i]; + } + + RadixItem_ *tmp(lhs); + lhs = rhs; + rhs = tmp; + } + + delete [] hist; + + NSMutableArray *values([NSMutableArray arrayWithCapacity:count]); + for (size_t i(0); i != count; ++i) + [values addObject:[self objectAtIndex:lhs[i].index]]; + [self setArray:values]; + + delete [] swap; +} + +@implementation NSMutableArray (Radix) + +- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object { + size_t count([self count]); + if (count == 0) + return; + +#if 0 + NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]); + [invocation setSelector:selector]; + [invocation setArgument:&object atIndex:2]; +#else + /* XXX: this is an unsafe optimization of doomy hell */ + Method method(class_getInstanceMethod([[self objectAtIndex:0] class], selector)); + _assert(method != NULL); + uint32_t (*imp)(id, SEL, id) = reinterpret_cast(method_getImplementation(method)); + _assert(imp != NULL); +#endif + + struct RadixItem_ *swap(new RadixItem_[count * 2]); + + for (size_t i(0); i != count; ++i) { + RadixItem_ &item(swap[i]); + item.index = i; + + id object([self objectAtIndex:i]); + +#if 0 + [invocation setTarget:object]; + [invocation invoke]; + [invocation getReturnValue:&item.key]; +#else + item.key = imp(object, selector, object); +#endif + } + + RadixSort_(self, count, swap); +} + +- (void) radixSortUsingFunction:(SKRadixFunction)function withContext:(void *)argument { + size_t count([self count]); + struct RadixItem_ *swap(new RadixItem_[count * 2]); + + for (size_t i(0); i != count; ++i) { + RadixItem_ &item(swap[i]); + item.index = i; + + id object([self objectAtIndex:i]); + item.key = function(object, argument); + } + + RadixSort_(self, count, swap); +} + +@end +/* }}} */ +/* Insertion Sort {{{ */ + +CFIndex SKBSearch_(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) { + const char *ptr = (const char *)list; + while (0 < count) { + CFIndex half = count / 2; + const char *probe = ptr + elementSize * half; + CFComparisonResult cr = comparator(element, probe, context); + if (0 == cr) return (probe - (const char *)list) / elementSize; + ptr = (cr < 0) ? ptr : probe + elementSize; + count = (cr < 0) ? half : (half + (count & 1) - 1); + } + return (ptr - (const char *)list) / elementSize; +} + +CFIndex CFBSearch_(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) { + const char *ptr = (const char *)list; + while (0 < count) { + CFIndex half = count / 2; + const char *probe = ptr + elementSize * half; + CFComparisonResult cr = comparator(element, probe, context); + if (0 == cr) return (probe - (const char *)list) / elementSize; + ptr = (cr < 0) ? ptr : probe + elementSize; + count = (cr < 0) ? half : (half + (count & 1) - 1); + } + return (ptr - (const char *)list) / elementSize; +} + +void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunction comparator, void *context) { + if (range.length == 0) + return; + const void **values(new const void *[range.length]); + CFArrayGetValues(array, range, values); + +#if HistogramInsertionSort + uint32_t total(0), *offsets(new uint32_t[range.length]); +#endif + + for (CFIndex index(1); index != range.length; ++index) { + const void *value(values[index]); + //CFIndex correct(SKBSearch_(&value, sizeof(const void *), values, index, comparator, context)); + CFIndex correct(index); + while (comparator(value, values[correct - 1], context) == kCFCompareLessThan) + if (--correct == 0) + break; + if (correct != index) { + size_t offset(index - correct); +#if HistogramInsertionSort + total += offset; + ++offsets[offset]; + if (offset > 10) + NSLog(@"Heavy Insertion Displacement: %u = %@", offset, value); +#endif + memmove(values + correct + 1, values + correct, sizeof(const void *) * offset); + values[correct] = value; + } + } + + CFArrayReplaceValues(array, range, values, range.length); + delete [] values; + +#if HistogramInsertionSort + for (CFIndex index(0); index != range.length; ++index) + if (offsets[index] != 0) + NSLog(@"Insertion Displacement [%u]: %u", index, offsets[index]); + NSLog(@"Average Insertion Displacement: %f", double(total) / range.length); + delete [] offsets; +#endif +} + +/* }}} */ + +/* Apple Bug Fixes {{{ */ +@implementation UIWebDocumentView (Cydia) + +- (void) _setScrollerOffset:(CGPoint)offset { + UIScroller *scroller([self _scroller]); + + CGSize size([scroller contentSize]); + CGSize bounds([scroller bounds].size); + + CGPoint max; + max.x = size.width - bounds.width; + max.y = size.height - bounds.height; + + // wtf Apple?! + if (max.x < 0) + max.x = 0; + if (max.y < 0) + max.y = 0; + + offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x; + offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y; + + [scroller setOffset:offset]; +} + +@end +/* }}} */ + +NSUInteger WebScriptObject$countByEnumeratingWithState$objects$count$(WebScriptObject *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) { + size_t length([self count] - state->state); + if (length <= 0) + return 0; + else if (length > count) + length = count; + for (size_t i(0); i != length; ++i) + objects[i] = [self objectAtIndex:state->state++]; + state->itemsPtr = objects; + state->mutationsPtr = (unsigned long *) self; + return length; +} + +NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) { + size_t length([self length] - state->state); + if (length <= 0) + return 0; + else if (length > count) + length = count; + for (size_t i(0); i != length; ++i) + objects[i] = [self item:state->state++]; + state->itemsPtr = objects; + state->mutationsPtr = (unsigned long *) self; + return length; +} + +@interface NSString (UIKit) +- (NSString *) stringByAddingPercentEscapes; +@end + +/* Cydia NSString Additions {{{ */ +@interface NSString (Cydia) ++ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length; ++ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length withZone:(NSZone *)zone inPool:(apr_pool_t *)pool; ++ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length; +- (NSComparisonResult) compareByPath:(NSString *)other; +- (NSString *) stringByCachingURLWithCurrentCDN; +- (NSString *) stringByAddingPercentEscapesIncludingReserved; +@end + +@implementation NSString (Cydia) + ++ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length { + return [[[NSString alloc] initWithBytesNoCopy:const_cast(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; +} + ++ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length withZone:(NSZone *)zone inPool:(apr_pool_t *)pool { + char *data(reinterpret_cast(apr_palloc(pool, length))); + memcpy(data, bytes, length); + return [[[NSString allocWithZone:zone] initWithBytesNoCopy:data length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; +} + ++ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length { + return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease]; +} + +- (NSComparisonResult) compareByPath:(NSString *)other { + NSString *prefix = [self commonPrefixWithString:other options:0]; + size_t length = [prefix length]; + + NSRange lrange = NSMakeRange(length, [self length] - length); + NSRange rrange = NSMakeRange(length, [other length] - length); + + lrange = [self rangeOfString:@"/" options:0 range:lrange]; + rrange = [other rangeOfString:@"/" options:0 range:rrange]; + + NSComparisonResult value; + + if (lrange.location == NSNotFound && rrange.location == NSNotFound) + value = NSOrderedSame; + else if (lrange.location == NSNotFound) + value = NSOrderedAscending; + else if (rrange.location == NSNotFound) + value = NSOrderedDescending; + else + value = NSOrderedSame; + + NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] : + [self substringWithRange:NSMakeRange(length, lrange.location - length)]; + NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] : + [other substringWithRange:NSMakeRange(length, rrange.location - length)]; + + NSComparisonResult result = [lpath compare:rpath]; + return result == NSOrderedSame ? value : result; +} + +- (NSString *) stringByCachingURLWithCurrentCDN { + return [self + stringByReplacingOccurrencesOfString:@"://cydia.saurik.com/" + withString:@"://cache.cydia.saurik.com/" + ]; +} + +- (NSString *) stringByAddingPercentEscapesIncludingReserved { + return [(id)CFURLCreateStringByAddingPercentEscapes( + kCFAllocatorDefault, + (CFStringRef) self, + NULL, + CFSTR(";/?:@&=+$,"), + kCFStringEncodingUTF8 + ) autorelease]; +} + +@end +/* }}} */ + +/* C++ NSString Wrapper Cache {{{ */ +class CYString { + private: + char *data_; + size_t size_; + CFStringRef cache_; + + _finline void clear_() { + if (cache_ != NULL) { + CFRelease(cache_); + cache_ = NULL; + } + } + + public: + _finline bool empty() const { + return size_ == 0; + } + + _finline size_t size() const { + return size_; + } + + _finline char *data() const { + return data_; + } + + _finline void clear() { + size_ = 0; + clear_(); + } + + _finline CYString() : + data_(0), + size_(0), + cache_(NULL) + { + } + + _finline ~CYString() { + clear_(); + } + + void operator =(const CYString &rhs) { + data_ = rhs.data_; + size_ = rhs.size_; + + if (rhs.cache_ == nil) + cache_ = NULL; + else + cache_ = reinterpret_cast(CFRetain(rhs.cache_)); + } + + void set(apr_pool_t *pool, const char *data, size_t size) { + if (size == 0) + clear(); + else { + clear_(); + + char *temp(reinterpret_cast(apr_palloc(pool, size + 1))); + memcpy(temp, data, size); + temp[size] = '\0'; + data_ = temp; + size_ = size; + } + } + + _finline void set(apr_pool_t *pool, const char *data) { + set(pool, data, data == NULL ? 0 : strlen(data)); + } + + _finline void set(apr_pool_t *pool, const std::string &rhs) { + set(pool, rhs.data(), rhs.size()); + } + + bool operator ==(const CYString &rhs) const { + return size_ == rhs.size_ && memcmp(data_, rhs.data_, size_) == 0; + } + + operator CFStringRef() { + if (cache_ == NULL) { + if (size_ == 0) + return nil; + cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast(data_), size_, kCFStringEncodingUTF8, NO, kCFAllocatorNull); + if (cache_ == NULL) + cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast(data_), size_, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull); + } return cache_; + } + + _finline operator id() { + return (NSString *) static_cast(*this); + } +}; +/* }}} */ +/* C++ NSString Algorithm Adapters {{{ */ +extern "C" { + CF_EXPORT CFHashCode CFStringHashNSString(CFStringRef str); +} + +struct NSStringMapHash : + std::unary_function +{ + _finline size_t operator ()(NSString *value) const { + return CFStringHashNSString((CFStringRef) value); + } +}; + +struct NSStringMapLess : + std::binary_function +{ + _finline bool operator ()(NSString *lhs, NSString *rhs) const { + return [lhs compare:rhs] == NSOrderedAscending; + } +}; + +struct NSStringMapEqual : + std::binary_function +{ + _finline bool operator ()(NSString *lhs, NSString *rhs) const { + return CFStringCompare((CFStringRef) lhs, (CFStringRef) rhs, 0) == kCFCompareEqualTo; + //CFEqual((CFTypeRef) lhs, (CFTypeRef) rhs); + //[lhs isEqualToString:rhs]; + } +}; +/* }}} */ + +/* Perl-Compatible RegEx {{{ */ +class Pcre { + private: + pcre *code_; + pcre_extra *study_; + int capture_; + int *matches_; + const char *data_; + + public: + Pcre(const char *regex) : + study_(NULL) + { + const char *error; + int offset; + code_ = pcre_compile(regex, 0, &error, &offset, NULL); + + if (code_ == NULL) { + lprintf("%d:%s\n", offset, error); + _assert(false); + } + + pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_); + matches_ = new int[(capture_ + 1) * 3]; + } + + ~Pcre() { + pcre_free(code_); + delete matches_; + } + + NSString *operator [](size_t match) { + return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])]; + } + + bool operator ()(NSString *data) { + // XXX: length is for characters, not for bytes + return operator ()([data UTF8String], [data length]); + } + + bool operator ()(const char *data, size_t size) { + data_ = data; + return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0; + } +}; +/* }}} */ +/* Mime Addresses {{{ */ +@interface Address : NSObject { + NSString *name_; + NSString *address_; +} + +- (NSString *) name; +- (NSString *) address; + +- (void) setAddress:(NSString *)address; + ++ (Address *) addressWithString:(NSString *)string; +- (Address *) initWithString:(NSString *)string; +@end + +@implementation Address + +- (void) dealloc { + [name_ release]; + if (address_ != nil) + [address_ release]; + [super dealloc]; +} + +- (NSString *) name { + return name_; +} + +- (NSString *) address { + return address_; +} + +- (void) setAddress:(NSString *)address { + if (address_ != nil) + [address_ autorelease]; + if (address == nil) + address_ = nil; + else + address_ = [address retain]; +} + ++ (Address *) addressWithString:(NSString *)string { + return [[[Address alloc] initWithString:string] autorelease]; +} + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects:@"address", @"name", nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (Address *) initWithString:(NSString *)string { + if ((self = [super init]) != nil) { + const char *data = [string UTF8String]; + size_t size = [string length]; + + static Pcre address_r("^\"?(.*)\"? <([^>]*)>$"); + + if (address_r(data, size)) { + name_ = [address_r[1] retain]; + address_ = [address_r[2] retain]; + } else { + name_ = [string retain]; + address_ = nil; + } + } return self; +} + +@end +/* }}} */ +/* CoreGraphics Primitives {{{ */ +class CGColor { + private: + CGColorRef color_; + + public: + CGColor() : + color_(NULL) + { + } + + CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) : + color_(NULL) + { + Set(space, red, green, blue, alpha); + } + + void Clear() { + if (color_ != NULL) + CGColorRelease(color_); + } + + ~CGColor() { + Clear(); + } + + void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) { + Clear(); + float color[] = {red, green, blue, alpha}; + color_ = CGColorCreate(space, color); + } + + operator CGColorRef() { + return color_; + } +}; +/* }}} */ + +/* Random Global Variables {{{ */ +static const int PulseInterval_ = 50000; +static const int ButtonBarWidth_ = 60; +static const int ButtonBarHeight_ = 48; +static const float KeyboardTime_ = 0.3f; + +static int Finish_; +static NSArray *Finishes_; + +#define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist" +#define NotifyConfig_ "/etc/notify.conf" + +static bool Queuing_; + +static CGColor Blue_; +static CGColor Blueish_; +static CGColor Black_; +static CGColor Off_; +static CGColor White_; +static CGColor Gray_; +static CGColor Green_; +static CGColor Purple_; +static CGColor Purplish_; + +static UIColor *InstallingColor_; +static UIColor *RemovingColor_; + +static NSString *App_; +static NSString *Home_; + +static BOOL Advanced_; +static BOOL Ignored_; + +static UIFont *Font12_; +static UIFont *Font12Bold_; +static UIFont *Font14_; +static UIFont *Font18Bold_; +static UIFont *Font22Bold_; + +static const char *Machine_ = NULL; +static const NSString *System_ = NULL; +static const NSString *SerialNumber_ = nil; +static const NSString *ChipID_ = nil; +static const NSString *Token_ = nil; +static const NSString *UniqueID_ = nil; +static const NSString *Build_ = nil; +static const NSString *Product_ = nil; +static const NSString *Safari_ = nil; + +static CFLocaleRef Locale_; +static NSArray *Languages_; +static CGColorSpaceRef space_; + +static bool reload_; + +static NSDictionary *SectionMap_; +static NSMutableDictionary *Metadata_; +static _transient NSMutableDictionary *Settings_; +static _transient NSString *Role_; +static _transient NSMutableDictionary *Packages_; +static _transient NSMutableDictionary *Sections_; +static _transient NSMutableDictionary *Sources_; +static bool Changed_; +static NSDate *now_; + +static bool IsWildcat_; + +#if RecycleWebViews +static NSMutableArray *Documents_; +#endif +/* }}} */ + +/* Display Helpers {{{ */ +inline float Interpolate(float begin, float end, float fraction) { + return (end - begin) * fraction + begin; +} + +/* XXX: localize this! */ +NSString *SizeString(double size) { + bool negative = size < 0; + if (negative) + size = -size; + + unsigned power = 0; + while (size > 1024) { + size /= 1024; + ++power; + } + + static const char *powers_[] = {"B", "kB", "MB", "GB"}; + + return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]]; +} + +static _finline CFStringRef CFCString(const char *value) { + return CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast(value), strlen(value), kCFStringEncodingUTF8, NO, kCFAllocatorNull); +} + +const char *StripVersion_(const char *version) { + const char *colon(strchr(version, ':')); + if (colon != NULL) + version = colon + 1; + return version; +} + +CFStringRef StripVersion(const char *version) { + const char *colon(strchr(version, ':')); + if (colon != NULL) + version = colon + 1; + return CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast(version), strlen(version), kCFStringEncodingUTF8, NO); + // XXX: performance + return CFCString(version); +} + +NSString *LocalizeSection(NSString *section) { + static Pcre title_r("^(.*?) \\((.*)\\)$"); + if (title_r(section)) { + NSString *parent(title_r[1]); + NSString *child(title_r[2]); + + return [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), + LocalizeSection(parent), + LocalizeSection(child) + ]; + } + + return [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"]; +} + +NSString *Simplify(NSString *title) { + const char *data = [title UTF8String]; + size_t size = [title length]; + + static Pcre square_r("^\\[(.*)\\]$"); + if (square_r(data, size)) + return Simplify(square_r[1]); + + static Pcre paren_r("^\\((.*)\\)$"); + if (paren_r(data, size)) + return Simplify(paren_r[1]); + + static Pcre title_r("^(.*?) \\((.*)\\)$"); + if (title_r(data, size)) + return Simplify(title_r[1]); + + return title; +} +/* }}} */ + +NSString *GetLastUpdate() { + NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]; + + if (update == nil) + return UCLocalize("NEVER_OR_UNKNOWN"); + + CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle); + CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update); + + CFRelease(formatter); + + return [(NSString *) formatted autorelease]; +} + +bool isSectionVisible(NSString *section) { + NSDictionary *metadata([Sections_ objectForKey:section]); + NSNumber *hidden(metadata == nil ? nil : [metadata objectForKey:@"Hidden"]); + return hidden == nil || ![hidden boolValue]; +} + +/* Delegate Prototypes {{{ */ +@class Package; +@class Source; + +@interface NSObject (ProgressDelegate) +@end + +@protocol ProgressDelegate +- (void) setProgressError:(NSString *)error withTitle:(NSString *)id; +- (void) setProgressTitle:(NSString *)title; +- (void) setProgressPercent:(float)percent; +- (void) startProgress; +- (void) addProgressOutput:(NSString *)output; +- (bool) isCancelling:(size_t)received; +@end + +@protocol ConfigurationDelegate +- (void) repairWithSelector:(SEL)selector; +- (void) setConfigurationData:(NSString *)data; +@end + +@class PackageView; + +@protocol CydiaDelegate +- (void) setPackageView:(PackageView *)view; +- (void) clearPackage:(Package *)package; +- (void) installPackage:(Package *)package; +- (void) installPackages:(NSArray *)packages; +- (void) removePackage:(Package *)package; +- (void) slideUp:(UIActionSheet *)alert; +- (void) distUpgrade; +- (void) updateData; +- (void) syncData; +- (void) askForSettings; +- (UIProgressHUD *) addProgressHUD; +- (void) removeProgressHUD:(UIProgressHUD *)hud; +- (RVPage *) pageForPackage:(NSString *)name; +- (PackageView *) packageView; +@end +/* }}} */ + +/* Status Delegation {{{ */ +class Status : + public pkgAcquireStatus +{ + private: + _transient NSObject *delegate_; + + public: + Status() : + delegate_(nil) + { + } + + void setDelegate(id delegate) { + delegate_ = delegate; + } + + NSObject *getDelegate() const { + return delegate_; + } + + virtual bool MediaChange(std::string media, std::string drive) { + return false; + } + + virtual void IMSHit(pkgAcquire::ItemDesc &item) { + } + + virtual void Fetch(pkgAcquire::ItemDesc &item) { + //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]); + [delegate_ setProgressTitle:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), [NSString stringWithUTF8String:item.ShortDesc.c_str()]]]; + } + + virtual void Done(pkgAcquire::ItemDesc &item) { + } + + virtual void Fail(pkgAcquire::ItemDesc &item) { + if ( + item.Owner->Status == pkgAcquire::Item::StatIdle || + item.Owner->Status == pkgAcquire::Item::StatDone + ) + return; + + std::string &error(item.Owner->ErrorText); + if (error.empty()) + return; + + NSString *description([NSString stringWithUTF8String:item.Description.c_str()]); + NSArray *fields([description componentsSeparatedByString:@" "]); + NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]); + + [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:) + withObject:[NSArray arrayWithObjects: + [NSString stringWithUTF8String:error.c_str()], + source, + nil] + waitUntilDone:YES + ]; + } + + virtual bool Pulse(pkgAcquire *Owner) { + bool value = pkgAcquireStatus::Pulse(Owner); + + float percent( + double(CurrentBytes + CurrentItems) / + double(TotalBytes + TotalItems) + ); + + [delegate_ setProgressPercent:percent]; + return [delegate_ isCancelling:CurrentBytes] ? false : value; + } + + virtual void Start() { + [delegate_ startProgress]; + } + + virtual void Stop() { + } +}; +/* }}} */ +/* Progress Delegation {{{ */ +class Progress : + public OpProgress +{ + private: + _transient id delegate_; + float percent_; + + protected: + virtual void Update() { + /*if (abs(Percent - percent_) > 2) + //NSLog(@"%s:%s:%f", Op.c_str(), SubOp.c_str(), Percent); + percent_ = Percent; + }*/ + + /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]]; + [delegate_ setProgressPercent:(Percent / 100)];*/ + } + + public: + Progress() : + delegate_(nil), + percent_(0) + { + } + + void setDelegate(id delegate) { + delegate_ = delegate; + } + + id getDelegate() const { + return delegate_; + } + + virtual void Done() { + //NSLog(@"DONE"); + //[delegate_ setProgressPercent:1]; + } +}; +/* }}} */ + +/* Database Interface {{{ */ +typedef std::map< unsigned long, _H > SourceMap; + +@interface Database : NSObject { + NSZone *zone_; + apr_pool_t *pool_; + + unsigned era_; + + pkgCacheFile cache_; + pkgDepCache::Policy *policy_; + pkgRecords *records_; + pkgProblemResolver *resolver_; + pkgAcquire *fetcher_; + FileFd *lock_; + SPtr manager_; + pkgSourceList *list_; + + SourceMap sources_; + NSMutableArray *packages_; + + _transient NSObject *delegate_; + Status status_; + Progress progress_; + + int cydiafd_; + int statusfd_; + FILE *input_; +} + ++ (Database *) sharedInstance; +- (unsigned) era; + +- (void) _readCydia:(NSNumber *)fd; +- (void) _readStatus:(NSNumber *)fd; +- (void) _readOutput:(NSNumber *)fd; + +- (FILE *) input; + +- (Package *) packageWithName:(NSString *)name; + +- (pkgCacheFile &) cache; +- (pkgDepCache::Policy *) policy; +- (pkgRecords *) records; +- (pkgProblemResolver *) resolver; +- (pkgAcquire &) fetcher; +- (pkgSourceList &) list; +- (NSArray *) packages; +- (NSArray *) sources; +- (void) reloadData; + +- (void) configure; +- (bool) prepare; +- (void) perform; +- (bool) upgrade; +- (void) update; + +- (void) setVisible; + +- (void) updateWithStatus:(Status &)status; + +- (void) setDelegate:(id)delegate; +- (Source *) getSource:(pkgCache::PkgFileIterator)file; +@end +/* }}} */ +/* Delegate Helpers {{{ */ +@implementation NSObject(ProgressDelegate) + +- (void) _setProgressErrorPackage:(NSArray *)args { + [self performSelector:@selector(setProgressError:forPackage:) + withObject:[args objectAtIndex:0] + withObject:([args count] == 1 ? nil : [args objectAtIndex:1]) + ]; +} + +- (void) _setProgressErrorTitle:(NSArray *)args { + [self performSelector:@selector(setProgressError:withTitle:) + withObject:[args objectAtIndex:0] + withObject:([args count] == 1 ? nil : [args objectAtIndex:1]) + ]; +} + +- (void) _setProgressError:(NSString *)error withTitle:(NSString *)title { + [self performSelectorOnMainThread:@selector(_setProgressErrorTitle:) + withObject:[NSArray arrayWithObjects:error, title, nil] + waitUntilDone:YES + ]; +} + +- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { + Package *package = id == nil ? nil : [[Database sharedInstance] packageWithName:id]; + // XXX: holy typecast batman! + [(id)self setProgressError:error withTitle:(package == nil ? id : [package name])]; +} + +@end +/* }}} */ + +/* Source Class {{{ */ +@interface Source : NSObject { + CYString depiction_; + CYString description_; + CYString label_; + CYString origin_; + CYString support_; + + CYString uri_; + CYString distribution_; + CYString type_; + CYString version_; + + NSString *host_; + NSString *authority_; + + CYString defaultIcon_; + + NSDictionary *record_; + BOOL trusted_; +} + +- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool; + +- (NSComparisonResult) compareByNameAndType:(Source *)source; + +- (NSString *) depictionForPackage:(NSString *)package; +- (NSString *) supportForPackage:(NSString *)package; + +- (NSDictionary *) record; +- (BOOL) trusted; + +- (NSString *) uri; +- (NSString *) distribution; +- (NSString *) type; +- (NSString *) key; +- (NSString *) host; + +- (NSString *) name; +- (NSString *) description; +- (NSString *) label; +- (NSString *) origin; +- (NSString *) version; + +- (NSString *) defaultIcon; + +@end + +@implementation Source + +- (void) _clear { + uri_.clear(); + distribution_.clear(); + type_.clear(); + + description_.clear(); + label_.clear(); + origin_.clear(); + depiction_.clear(); + support_.clear(); + version_.clear(); + defaultIcon_.clear(); + + if (record_ != nil) { + [record_ release]; + record_ = nil; + } + + if (host_ != nil) { + [host_ release]; + host_ = nil; + } + + if (authority_ != nil) { + [authority_ release]; + authority_ = nil; + } +} + +- (void) dealloc { + [self _clear]; + [super dealloc]; +} + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool { + [self _clear]; + + trusted_ = index->IsTrusted(); + + uri_.set(pool, index->GetURI()); + distribution_.set(pool, index->GetDist()); + type_.set(pool, index->GetType()); + + debReleaseIndex *dindex(dynamic_cast(index)); + if (dindex != NULL) { + FileFd fd; + if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly)) + _error->Discard(); + else { + pkgTagFile tags(&fd); + + pkgTagSection section; + tags.Step(section); + + struct { + const char *name_; + CYString *value_; + } names[] = { + {"default-icon", &defaultIcon_}, + {"depiction", &depiction_}, + {"description", &description_}, + {"label", &label_}, + {"origin", &origin_}, + {"support", &support_}, + {"version", &version_}, + }; + + for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) { + const char *start, *end; + + if (section.Find(names[i].name_, start, end)) { + CYString &value(*names[i].value_); + value.set(pool, start, end - start); + } + } + } + } + + record_ = [Sources_ objectForKey:[self key]]; + if (record_ != nil) + record_ = [record_ retain]; + + NSURL *url([NSURL URLWithString:uri_]); + + host_ = [url host]; + if (host_ != nil) + host_ = [[host_ lowercaseString] retain]; + + if (host_ != nil) + authority_ = host_; + else + authority_ = [url path]; + + if (authority_ != nil) + authority_ = [authority_ retain]; +} + +- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool { + if ((self = [super init]) != nil) { + [self setMetaIndex:index inPool:pool]; + } return self; +} + +- (NSComparisonResult) compareByNameAndType:(Source *)source { + NSDictionary *lhr = [self record]; + NSDictionary *rhr = [source record]; + + if (lhr != rhr) + return lhr == nil ? NSOrderedDescending : NSOrderedAscending; + + NSString *lhs = [self name]; + NSString *rhs = [source name]; + + if ([lhs length] != 0 && [rhs length] != 0) { + unichar lhc = [lhs characterAtIndex:0]; + unichar rhc = [rhs characterAtIndex:0]; + + if (isalpha(lhc) && !isalpha(rhc)) + return NSOrderedAscending; + else if (!isalpha(lhc) && isalpha(rhc)) + return NSOrderedDescending; + } + + return [lhs compare:rhs options:LaxCompareOptions_]; +} + +- (NSString *) depictionForPackage:(NSString *)package { + return depiction_.empty() ? nil : [depiction_ stringByReplacingOccurrencesOfString:@"*" withString:package]; +} + +- (NSString *) supportForPackage:(NSString *)package { + return support_.empty() ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package]; +} + +- (NSDictionary *) record { + return record_; +} + +- (BOOL) trusted { + return trusted_; +} + +- (NSString *) uri { + return uri_; +} + +- (NSString *) distribution { + return distribution_; +} + +- (NSString *) type { + return type_; +} + +- (NSString *) key { + return [NSString stringWithFormat:@"%@:%@:%@", (NSString *) type_, (NSString *) uri_, (NSString *) distribution_]; +} + +- (NSString *) host { + return host_; +} + +- (NSString *) name { + return origin_.empty() ? authority_ : origin_; +} + +- (NSString *) description { + return description_; +} + +- (NSString *) label { + return label_.empty() ? authority_ : label_; +} + +- (NSString *) origin { + return origin_; +} + +- (NSString *) version { + return version_; +} + +- (NSString *) defaultIcon { + return defaultIcon_; +} + +@end +/* }}} */ +/* Relationship Class {{{ */ +@interface Relationship : NSObject { + NSString *type_; + NSString *id_; +} + +- (NSString *) type; +- (NSString *) id; +- (NSString *) name; + +@end + +@implementation Relationship + +- (void) dealloc { + [type_ release]; + [id_ release]; + [super dealloc]; +} + +- (NSString *) type { + return type_; +} + +- (NSString *) id { + return id_; +} + +- (NSString *) name { + _assert(false); + return nil; +} + +@end +/* }}} */ +/* Package Class {{{ */ +@interface Package : NSObject { + unsigned era_; + apr_pool_t *pool_; + + pkgCache::VerIterator version_; + pkgCache::PkgIterator iterator_; + _transient Database *database_; + pkgCache::VerFileIterator file_; + + Source *source_; + bool cached_; + bool parsed_; + + CYString section_; + NSString *section$_; + bool essential_; + bool required_; + bool visible_; + bool obsolete_; + + NSString *latest_; + CYString installed_; + + CYString id_; + CYString name_; + CYString tagline_; + CYString icon_; + CYString depiction_; + CYString homepage_; + + CYString sponsor_; + Address *sponsor$_; + + CYString author_; + Address *author$_; + + CYString bugs_; + CYString support_; + NSMutableArray *tags_; + NSString *role_; + + NSArray *relationships_; + + NSMutableDictionary *metadata_; + _transient NSDate *firstSeen_; + _transient NSDate *lastSeen_; + bool subscribed_; +} + +- (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database; ++ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database; + +- (pkgCache::PkgIterator) iterator; +- (void) parse; + +- (NSString *) section; +- (NSString *) simpleSection; + +- (NSString *) longSection; +- (NSString *) shortSection; + +- (NSString *) uri; + +- (Address *) maintainer; +- (size_t) size; +- (NSString *) longDescription; +- (NSString *) shortDescription; +- (unichar) index; + +- (NSMutableDictionary *) metadata; +- (NSDate *) seen; +- (BOOL) subscribed; +- (BOOL) ignored; + +- (NSString *) latest; +- (NSString *) installed; +- (BOOL) uninstalled; + +- (BOOL) valid; +- (BOOL) upgradableAndEssential:(BOOL)essential; +- (BOOL) essential; +- (BOOL) broken; +- (BOOL) unfiltered; +- (BOOL) visible; + +- (BOOL) half; +- (BOOL) halfConfigured; +- (BOOL) halfInstalled; +- (BOOL) hasMode; +- (NSString *) mode; + +- (void) setVisible; + +- (NSString *) id; +- (NSString *) name; +- (UIImage *) icon; +- (NSString *) homepage; +- (NSString *) depiction; +- (Address *) author; + +- (NSString *) support; + +- (NSArray *) files; +- (NSArray *) relationships; +- (NSArray *) warnings; +- (NSArray *) applications; + +- (Source *) source; +- (NSString *) role; + +- (BOOL) matches:(NSString *)text; + +- (bool) hasSupportingRole; +- (BOOL) hasTag:(NSString *)tag; +- (NSString *) primaryPurpose; +- (NSArray *) purposes; +- (bool) isCommercial; + +- (CYString &) cyname; + +- (uint32_t) compareBySection:(NSArray *)sections; + +- (uint32_t) compareForChanges; + +- (void) install; +- (void) remove; + +- (bool) isUnfilteredAndSearchedForBy:(NSString *)search; +- (bool) isUnfilteredAndSelectedForBy:(NSString *)search; +- (bool) isInstalledAndVisible:(NSNumber *)number; +- (bool) isVisibleInSection:(NSString *)section; +- (bool) isVisibleInSource:(Source *)source; + +@end + +uint32_t PackageChangesRadix(Package *self, void *) { + union { + uint32_t key; + + struct { + uint32_t timestamp : 30; + uint32_t ignored : 1; + uint32_t upgradable : 1; + } bits; + } value; + + bool upgradable([self upgradableAndEssential:YES]); + value.bits.upgradable = upgradable ? 1 : 0; + + if (upgradable) { + value.bits.timestamp = 0; + value.bits.ignored = [self ignored] ? 0 : 1; + value.bits.upgradable = 1; + } else { + value.bits.timestamp = static_cast([[self seen] timeIntervalSince1970]) >> 2; + value.bits.ignored = 0; + value.bits.upgradable = 0; + } + + return _not(uint32_t) - value.key; +} + +_finline static void Stifle(uint8_t &value) { +} + +uint32_t PackagePrefixRadix(Package *self, void *context) { + size_t offset(reinterpret_cast(context)); + CYString &name([self cyname]); + + size_t size(name.size()); + if (size == 0) + return 0; + char *text(name.data()); + + size_t zeros; + if (!isdigit(text[0])) + zeros = 0; + else { + size_t digits(1); + while (size != digits && isdigit(text[digits])) + if (++digits == 4) + break; + zeros = 4 - digits; + } + + uint8_t data[4]; + + // 0.607997 + + if (offset == 0 && zeros != 0) { + memset(data, '0', zeros); + memcpy(data + zeros, text, 4 - zeros); + } else { + /* XXX: there's some danger here if you request a non-zero offset < 4 and it gets zero padded */ + if (size <= offset - zeros) + return 0; + + text += offset - zeros; + size -= offset - zeros; + + if (size >= 4) + memcpy(data, text, 4); + else { + memcpy(data, text, size); + memset(data + size, 0, 4 - size); + } + + for (size_t i(0); i != 4; ++i) + if (isalpha(data[i])) + data[i] &= 0xdf; + } + + if (offset == 0) + data[0] = (data[0] & 0x3f) | "\x80\x00\xc0\x40"[data[0] >> 6]; + + /* XXX: ntohl may be more honest */ + return OSSwapInt32(*reinterpret_cast(data)); +} + +CYString &(*PackageName)(Package *self, SEL sel); + +CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) { + _profile(PackageNameCompare) + CYString &lhi(PackageName(lhs, @selector(cyname))); + CYString &rhi(PackageName(rhs, @selector(cyname))); + CFStringRef lhn(lhi), rhn(rhi); + + if (lhn == NULL) + return rhn == NULL ? NSOrderedSame : NSOrderedAscending; + else if (rhn == NULL) + return NSOrderedDescending; + + _profile(PackageNameCompare$NumbersLast) + if (!lhi.empty() && !rhi.empty()) { + UniChar lhc(CFStringGetCharacterAtIndex(lhn, 0)); + UniChar rhc(CFStringGetCharacterAtIndex(rhn, 0)); + bool lha(CFUniCharIsMemberOf(lhc, kCFUniCharLetterCharacterSet)); + if (lha != CFUniCharIsMemberOf(rhc, kCFUniCharLetterCharacterSet)) + return lha ? NSOrderedAscending : NSOrderedDescending; + } + _end + + CFIndex length = CFStringGetLength(lhn); + + _profile(PackageNameCompare$Compare) + return CFStringCompareWithOptionsAndLocale(lhn, rhn, CFRangeMake(0, length), LaxCompareFlags_, Locale_); + _end + _end +} + +CFComparisonResult PackageNameCompare_(Package **lhs, Package **rhs, void *context) { + return PackageNameCompare(*lhs, *rhs, context); +} + +struct PackageNameOrdering : + std::binary_function +{ + _finline bool operator ()(Package *lhs, Package *rhs) const { + return PackageNameCompare(lhs, rhs, NULL) == NSOrderedAscending; + } +}; + +@implementation Package + +- (NSString *) description { + return [NSString stringWithFormat:@"", static_cast(name_)]; +} + +- (void) dealloc { + if (source_ != nil) + [source_ release]; + if (section$_ != nil) + [section$_ release]; + + if (latest_ != nil) + [latest_ release]; + + if (sponsor$_ != nil) + [sponsor$_ release]; + if (author$_ != nil) + [author$_ release]; + if (tags_ != nil) + [tags_ release]; + if (role_ != nil) + [role_ release]; + + if (relationships_ != nil) + [relationships_ release]; + if (metadata_ != nil) + [metadata_ release]; + + [super dealloc]; +} + ++ (NSString *) webScriptNameForSelector:(SEL)selector { + if (selector == @selector(hasTag:)) + return @"hasTag"; + else + return nil; +} + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { + return [self webScriptNameForSelector:selector] == nil; +} + ++ (NSArray *) _attributeKeys { + 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]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (void) parse { + if (parsed_) + return; + parsed_ = true; + if (file_.end()) + return; + + _profile(Package$parse) + pkgRecords::Parser *parser; + + _profile(Package$parse$Lookup) + parser = &[database_ records]->Lookup(file_); + _end + + CYString website; + + _profile(Package$parse$Find) + struct { + const char *name_; + CYString *value_; + } names[] = { + {"icon", &icon_}, + {"depiction", &depiction_}, + {"homepage", &homepage_}, + {"website", &website}, + {"bugs", &bugs_}, + {"support", &support_}, + {"sponsor", &sponsor_}, + {"author", &author_}, + }; + + for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) { + const char *start, *end; + + if (parser->Find(names[i].name_, start, end)) { + CYString &value(*names[i].value_); + _profile(Package$parse$Value) + value.set(pool_, start, end - start); + _end + } + } + _end + + _profile(Package$parse$Tagline) + const char *start, *end; + if (parser->ShortDesc(start, end)) { + const char *stop(reinterpret_cast(memchr(start, '\n', end - start))); + if (stop == NULL) + stop = end; + while (stop != start && stop[-1] == '\r') + --stop; + tagline_.set(pool_, start, stop - start); + } + _end + + _profile(Package$parse$Retain) + if (homepage_.empty()) + homepage_ = website; + if (homepage_ == depiction_) + homepage_.clear(); + _end + _end +} + +- (void) setVisible { + visible_ = required_ && [self unfiltered]; +} + +- (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database { + if ((self = [super init]) != nil) { + _profile(Package$initWithVersion) + @synchronized (database) { + era_ = [database era]; + pool_ = pool; + + version_ = version; + iterator_ = version.ParentPkg(); + database_ = database; + + _profile(Package$initWithVersion$Latest) + latest_ = (NSString *) StripVersion(version_.VerStr()); + _end + + pkgCache::VerIterator current; + _profile(Package$initWithVersion$Versions) + current = iterator_.CurrentVer(); + if (!current.end()) + installed_.set(pool_, StripVersion_(current.VerStr())); + + if (!version_.end()) + file_ = version_.FileList(); + else { + pkgCache &cache([database_ cache]); + file_ = pkgCache::VerFileIterator(cache, cache.VerFileP); + } + _end + + _profile(Package$initWithVersion$Name) + id_.set(pool_, iterator_.Name()); + name_.set(pool, iterator_.Display()); + _end + + if (!file_.end()) { + _profile(Package$initWithVersion$Source) + source_ = [database_ getSource:file_.File()]; + if (source_ != nil) + [source_ retain]; + cached_ = true; + _end + } + + required_ = true; + + _profile(Package$initWithVersion$Tags) + pkgCache::TagIterator tag(iterator_.TagList()); + if (!tag.end()) { + tags_ = [[NSMutableArray alloc] initWithCapacity:8]; + do { + const char *name(tag.Name()); + [tags_ addObject:(NSString *)CFCString(name)]; + if (role_ == nil && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/) + role_ = (NSString *) CFCString(name + 6); + if (required_ && strncmp(name, "require::", 9) == 0 && ( + true + )) + required_ = false; + ++tag; + } while (!tag.end()); + } + _end + + bool changed(false); + NSString *key([id_ lowercaseString]); + + _profile(Package$initWithVersion$Metadata) + metadata_ = [Packages_ objectForKey:key]; + + if (metadata_ == nil) { + firstSeen_ = now_; + + metadata_ = [[NSMutableDictionary dictionaryWithObjectsAndKeys: + firstSeen_, @"FirstSeen", + latest_, @"LastVersion", + nil] mutableCopy]; + + changed = true; + } else { + firstSeen_ = [metadata_ objectForKey:@"FirstSeen"]; + lastSeen_ = [metadata_ objectForKey:@"LastSeen"]; + + if (NSNumber *subscribed = [metadata_ objectForKey:@"IsSubscribed"]) + subscribed_ = [subscribed boolValue]; + + NSString *version([metadata_ objectForKey:@"LastVersion"]); + + if (firstSeen_ == nil) { + firstSeen_ = lastSeen_ == nil ? now_ : lastSeen_; + [metadata_ setObject:firstSeen_ forKey:@"FirstSeen"]; + changed = true; + } + + if (version == nil) { + [metadata_ setObject:latest_ forKey:@"LastVersion"]; + changed = true; + } else if (![version isEqualToString:latest_]) { + [metadata_ setObject:latest_ forKey:@"LastVersion"]; + lastSeen_ = now_; + [metadata_ setObject:lastSeen_ forKey:@"LastSeen"]; + changed = true; + } + } + + metadata_ = [metadata_ retain]; + + if (changed) { + [Packages_ setObject:metadata_ forKey:key]; + Changed_ = true; + } + _end + + _profile(Package$initWithVersion$Section) + section_.set(pool_, iterator_.Section()); + _end + + obsolete_ = [self hasTag:@"cydia::obsolete"]; + essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"]; + [self setVisible]; + } _end } return self; +} + ++ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database { +@synchronized ([Database class]) { + pkgCache::VerIterator version; + + _profile(Package$packageWithIterator$GetCandidateVer) + version = [database policy]->GetCandidateVer(iterator); + _end + + if (version.end()) + return nil; + + return [[[Package alloc] + initWithVersion:version + withZone:zone + inPool:pool + database:database + ] autorelease]; +} } + +- (pkgCache::PkgIterator) iterator { + return iterator_; +} + +- (NSString *) section { + if (section$_ == nil) { + if (section_.empty()) + return nil; + + std::replace(section_.data(), section_.data() + section_.size(), ' ', '_'); + NSString *name(section_); + + lookup: + if (NSDictionary *value = [SectionMap_ objectForKey:name]) + if (NSString *rename = [value objectForKey:@"Rename"]) { + name = rename; + goto lookup; + } + + section$_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain]; + } return section$_; +} + +- (NSString *) simpleSection { + if (NSString *section = [self section]) + return Simplify(section); + else + return nil; +} + +- (NSString *) longSection { + return LocalizeSection([self section]); +} + +- (NSString *) shortSection { + return [[NSBundle mainBundle] localizedStringForKey:[self simpleSection] value:nil table:@"Sections"]; +} + +- (NSString *) uri { + return nil; +#if 0 + pkgIndexFile *index; + pkgCache::PkgFileIterator file(file_.File()); + if (![database_ list].FindIndex(file, index)) + return nil; + return [NSString stringWithUTF8String:iterator_->Path]; + //return [NSString stringWithUTF8String:file.Site()]; + //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()]; +#endif +} + +- (Address *) maintainer { + if (file_.end()) + return nil; + pkgRecords::Parser *parser = &[database_ records]->Lookup(file_); + const std::string &maintainer(parser->Maintainer()); + return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]]; +} + +- (size_t) size { + return version_.end() ? 0 : version_->InstalledSize; +} + +- (NSString *) longDescription { + if (file_.end()) + return nil; + pkgRecords::Parser *parser = &[database_ records]->Lookup(file_); + NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]); + + NSArray *lines = [description componentsSeparatedByString:@"\n"]; + NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)]; + if ([lines count] < 2) + return nil; + + NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet]; + for (size_t i(1), e([lines count]); i != e; ++i) { + NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace]; + [trimmed addObject:trim]; + } + + return [trimmed componentsJoinedByString:@"\n"]; +} + +- (NSString *) shortDescription { + return tagline_; +} + +- (unichar) index { + _profile(Package$index) + CFStringRef name((CFStringRef) [self name]); + if (CFStringGetLength(name) == 0) + return '#'; + UniChar character(CFStringGetCharacterAtIndex(name, 0)); + if (!CFUniCharIsMemberOf(character, kCFUniCharLetterCharacterSet)) + return '#'; + return toupper(character); + _end +} + +- (NSMutableDictionary *) metadata { + return metadata_; +} + +- (NSDate *) seen { + if (subscribed_ && lastSeen_ != nil) + return lastSeen_; + return firstSeen_; +} + +- (BOOL) subscribed { + return subscribed_; +} + +- (BOOL) ignored { + NSDictionary *metadata([self metadata]); + if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"]) + return [ignored boolValue]; + else + return false; +} + +- (NSString *) latest { + return latest_; +} + +- (NSString *) installed { + return installed_; +} + +- (BOOL) uninstalled { + 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_ && visible_; + else + return !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep()); + _end +} + +- (BOOL) essential { + return essential_; +} + +- (BOOL) broken { + return [database_ cache][iterator_].InstBroken(); +} + +- (BOOL) unfiltered { + NSString *section([self section]); + return !obsolete_ && [self hasSupportingRole] && (section == nil || isSectionVisible(section)); +} + +- (BOOL) visible { + return visible_; +} + +- (BOOL) half { + unsigned char current(iterator_->CurrentState); + return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled; +} + +- (BOOL) halfConfigured { + return iterator_->CurrentState == pkgCache::State::HalfConfigured; +} + +- (BOOL) halfInstalled { + return iterator_->CurrentState == pkgCache::State::HalfInstalled; +} + +- (BOOL) hasMode { + pkgDepCache::StateCache &state([database_ cache][iterator_]); + return state.Mode != pkgDepCache::ModeKeep; +} + +- (NSString *) mode { + pkgDepCache::StateCache &state([database_ cache][iterator_]); + + switch (state.Mode) { + case pkgDepCache::ModeDelete: + if ((state.iFlags & pkgDepCache::Purge) != 0) + return @"PURGE"; + else + return @"REMOVE"; + case pkgDepCache::ModeKeep: + if ((state.iFlags & pkgDepCache::ReInstall) != 0) + return @"REINSTALL"; + /*else if ((state.iFlags & pkgDepCache::AutoKept) != 0) + return nil;*/ + else + return nil; + case pkgDepCache::ModeInstall: + /*if ((state.iFlags & pkgDepCache::ReInstall) != 0) + return @"REINSTALL"; + else*/ switch (state.Status) { + case -1: + return @"DOWNGRADE"; + case 0: + return @"INSTALL"; + case 1: + return @"UPGRADE"; + case 2: + return @"NEW_INSTALL"; + _nodefault + } + _nodefault + } +} + +- (NSString *) id { + return id_; +} + +- (NSString *) name { + return name_.empty() ? id_ : name_; +} + +- (UIImage *) icon { + NSString *section = [self simpleSection]; + + UIImage *icon(nil); + if (!icon_.empty()) + if ([icon_ hasPrefix:@"file:///"]) + icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]]; + if (icon == nil) if (section != nil) + icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]; + if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon]) + if ([dicon hasPrefix:@"file:///"]) + icon = [UIImage imageAtPath:[dicon substringFromIndex:7]]; + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + return icon; +} + +- (NSString *) homepage { + return homepage_; +} + +- (NSString *) depiction { + return !depiction_.empty() ? depiction_ : [[self source] depictionForPackage:id_]; +} + +- (Address *) sponsor { + if (sponsor$_ == nil) { + if (sponsor_.empty()) + return nil; + sponsor$_ = [[Address addressWithString:sponsor_] retain]; + } return sponsor$_; +} + +- (Address *) author { + if (author$_ == nil) { + if (author_.empty()) + return nil; + author$_ = [[Address addressWithString:author_] retain]; + } return author$_; +} + +- (NSString *) support { + return !bugs_.empty() ? bugs_ : [[self source] supportForPackage:id_]; +} + +- (NSArray *) files { + NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", static_cast(id_)]; + NSMutableArray *files = [NSMutableArray arrayWithCapacity:128]; + + std::ifstream fin; + fin.open([path UTF8String]); + if (!fin.is_open()) + return nil; + + std::string line; + while (std::getline(fin, line)) + [files addObject:[NSString stringWithUTF8String:line.c_str()]]; + + return files; +} + +- (NSArray *) relationships { + return relationships_; +} + +- (NSArray *) warnings { + NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]); + const char *name(iterator_.Name()); + + size_t length(strlen(name)); + if (length < 2) invalid: + [warnings addObject:UCLocalize("ILLEGAL_PACKAGE_IDENTIFIER")]; + else for (size_t i(0); i != length; ++i) + if ( + /* XXX: technically this is not allowed */ + (name[i] < 'A' || name[i] > 'Z') && + (name[i] < 'a' || name[i] > 'z') && + (name[i] < '0' || name[i] > '9') && + (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.') + ) goto invalid; + + if (strcmp(name, "cydia") != 0) { + bool cydia = false; + bool user = false; + bool _private = false; + bool stash = false; + + bool repository = [[self section] isEqualToString:@"Repositories"]; + + if (NSArray *files = [self files]) + for (NSString *file in files) + if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"]) + cydia = true; + else if (!user && [file isEqualToString:@"/User"]) + user = true; + else if (!_private && [file isEqualToString:@"/private"]) + _private = true; + else if (!stash && [file isEqualToString:@"/var/stash"]) + stash = true; + + /* XXX: this is not sensitive enough. only some folders are valid. */ + if (cydia && !repository) + [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"Cydia.app"]]; + if (user) + [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/User"]]; + if (_private) + [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/private"]]; + if (stash) + [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/var/stash"]]; + } + + return [warnings count] == 0 ? nil : warnings; +} + +- (NSArray *) applications { + NSString *me([[NSBundle mainBundle] bundleIdentifier]); + + NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]); + + static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$"); + if (NSArray *files = [self files]) + for (NSString *file in files) + if (application_r(file)) { + NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]); + NSString *id([info objectForKey:@"CFBundleIdentifier"]); + if ([id isEqualToString:me]) + continue; + + NSString *display([info objectForKey:@"CFBundleDisplayName"]); + if (display == nil) + display = application_r[1]; + + NSString *bundle([file stringByDeletingLastPathComponent]); + NSString *icon([info objectForKey:@"CFBundleIconFile"]); + if (icon == nil || [icon length] == 0) + icon = @"icon.png"; + NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]); + + NSMutableArray *application([NSMutableArray arrayWithCapacity:2]); + [applications addObject:application]; + + [application addObject:id]; + [application addObject:display]; + [application addObject:url]; + } + + return [applications count] == 0 ? nil : applications; +} + +- (Source *) source { + if (!cached_) { + @synchronized (database_) { + if ([database_ era] != era_ || file_.end()) + source_ = nil; + else { + source_ = [database_ getSource:file_.File()]; + if (source_ != nil) + [source_ retain]; + } + + cached_ = true; + } + } + + return source_; +} + +- (NSString *) role { + return role_; +} + +- (BOOL) matches:(NSString *)text { + if (text == nil) + return NO; + + NSRange range; + + range = [[self id] rangeOfString:text options:MatchCompareOptions_]; + if (range.location != NSNotFound) + return YES; + + range = [[self name] rangeOfString:text options:MatchCompareOptions_]; + if (range.location != NSNotFound) + return YES; + + range = [[self shortDescription] rangeOfString:text options:MatchCompareOptions_]; + if (range.location != NSNotFound) + return YES; + + return NO; +} + +- (bool) hasSupportingRole { + if (role_ == nil) + return true; + if ([role_ isEqualToString:@"enduser"]) + return true; + if ([Role_ isEqualToString:@"User"]) + return false; + if ([role_ isEqualToString:@"hacker"]) + return true; + if ([Role_ isEqualToString:@"Hacker"]) + return false; + if ([role_ isEqualToString:@"developer"]) + return true; + if ([Role_ isEqualToString:@"Developer"]) + return false; + _assert(false); +} + +- (BOOL) hasTag:(NSString *)tag { + return tags_ == nil ? NO : [tags_ containsObject:tag]; +} + +- (NSString *) primaryPurpose { + for (NSString *tag in tags_) + if ([tag hasPrefix:@"purpose::"]) + return [tag substringFromIndex:9]; + return nil; +} + +- (NSArray *) purposes { + NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]); + for (NSString *tag in tags_) + if ([tag hasPrefix:@"purpose::"]) + [purposes addObject:[tag substringFromIndex:9]]; + return [purposes count] == 0 ? nil : purposes; +} + +- (bool) isCommercial { + return [self hasTag:@"cydia::commercial"]; +} + +- (CYString &) cyname { + return name_.empty() ? id_ : name_; +} + +- (uint32_t) compareBySection:(NSArray *)sections { + NSString *section([self section]); + for (size_t i(0), e([sections count]); i != e; ++i) { + if ([section isEqualToString:[[sections objectAtIndex:i] name]]) + return i; + } + + return _not(uint32_t); +} + +- (uint32_t) compareForChanges { + union { + uint32_t key; + + struct { + uint32_t timestamp : 30; + uint32_t ignored : 1; + uint32_t upgradable : 1; + } bits; + } value; + + bool upgradable([self upgradableAndEssential:YES]); + value.bits.upgradable = upgradable ? 1 : 0; + + if (upgradable) { + value.bits.timestamp = 0; + value.bits.ignored = [self ignored] ? 0 : 1; + value.bits.upgradable = 1; + } else { + value.bits.timestamp = static_cast([[self seen] timeIntervalSince1970]) >> 2; + value.bits.ignored = 0; + value.bits.upgradable = 0; + } + + return _not(uint32_t) - value.key; +} + +- (void) clear { + pkgProblemResolver *resolver = [database_ resolver]; + resolver->Clear(iterator_); + resolver->Protect(iterator_); +} + +- (void) install { + pkgProblemResolver *resolver = [database_ resolver]; + resolver->Clear(iterator_); + resolver->Protect(iterator_); + pkgCacheFile &cache([database_ cache]); + cache->MarkInstall(iterator_, false); + pkgDepCache::StateCache &state((*cache)[iterator_]); + if (!state.Install()) + cache->SetReInstall(iterator_, true); +} + +- (void) remove { + pkgProblemResolver *resolver = [database_ resolver]; + resolver->Clear(iterator_); + resolver->Protect(iterator_); + resolver->Remove(iterator_); + [database_ cache]->MarkDelete(iterator_, true); +} + +- (bool) isUnfilteredAndSearchedForBy:(NSString *)search { + _profile(Package$isUnfilteredAndSearchedForBy) + bool value(true); + + _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered) + value &= [self unfiltered]; + _end + + _profile(Package$isUnfilteredAndSearchedForBy$Match) + value &= [self matches:search]; + _end + + return value; + _end +} + +- (bool) isUnfilteredAndSelectedForBy:(NSString *)search { + if ([search length] == 0) + return false; + + _profile(Package$isUnfilteredAndSelectedForBy) + bool value(true); + + _profile(Package$isUnfilteredAndSelectedForBy$Unfiltered) + value &= [self unfiltered]; + _end + + _profile(Package$isUnfilteredAndSelectedForBy$Match) + value &= [[self name] compare:search options:MatchCompareOptions_ range:NSMakeRange(0, [search length])] == NSOrderedSame; + _end + + return value; + _end +} + +- (bool) isInstalledAndVisible:(NSNumber *)number { + return (![number boolValue] || [self visible]) && ![self uninstalled]; +} + +- (bool) isVisibleInSection:(NSString *)name { + NSString *section = [self section]; + + return + [self visible] && ( + name == nil || + section == nil && [name length] == 0 || + [name isEqualToString:section] + ); +} + +- (bool) isVisibleInSource:(Source *)source { + return [self source] == source && [self visible]; +} + +@end +/* }}} */ +/* Section Class {{{ */ +@interface Section : NSObject { + NSString *name_; + unichar index_; + size_t row_; + size_t count_; + NSString *localized_; +} + +- (NSComparisonResult) compareByLocalized:(Section *)section; +- (Section *) initWithName:(NSString *)name localized:(NSString *)localized; +- (Section *) initWithName:(NSString *)name localize:(BOOL)localize; +- (Section *) initWithName:(NSString *)name row:(size_t)row localize:(BOOL)localize; +- (Section *) initWithIndex:(unichar)index row:(size_t)row; +- (NSString *) name; +- (unichar) index; + +- (size_t) row; +- (size_t) count; + +- (void) addToRow; +- (void) addToCount; + +- (void) setCount:(size_t)count; +- (NSString *) localized; + +@end + +@implementation Section + +- (void) dealloc { + [name_ release]; + if (localized_ != nil) + [localized_ release]; + [super dealloc]; +} + +- (NSComparisonResult) compareByLocalized:(Section *)section { + NSString *lhs(localized_); + NSString *rhs([section localized]); + + /*if ([lhs length] != 0 && [rhs length] != 0) { + unichar lhc = [lhs characterAtIndex:0]; + unichar rhc = [rhs characterAtIndex:0]; + + if (isalpha(lhc) && !isalpha(rhc)) + return NSOrderedAscending; + else if (!isalpha(lhc) && isalpha(rhc)) + return NSOrderedDescending; + }*/ + + return [lhs compare:rhs options:LaxCompareOptions_]; +} + +- (Section *) initWithName:(NSString *)name localized:(NSString *)localized { + if ((self = [self initWithName:name localize:NO]) != nil) { + if (localized != nil) + localized_ = [localized retain]; + } return self; +} + +- (Section *) initWithName:(NSString *)name localize:(BOOL)localize { + return [self initWithName:name row:0 localize:localize]; +} + +- (Section *) initWithName:(NSString *)name row:(size_t)row localize:(BOOL)localize { + if ((self = [super init]) != nil) { + name_ = [name retain]; + index_ = '\0'; + row_ = row; + if (localize) + localized_ = [LocalizeSection(name_) retain]; + } return self; +} + +/* XXX: localize the index thingees */ +- (Section *) initWithIndex:(unichar)index row:(size_t)row { + if ((self = [super init]) != nil) { + name_ = [[NSString stringWithCharacters:&index length:1] retain]; + index_ = index; + row_ = row; + } return self; +} + +- (NSString *) name { + return name_; +} + +- (unichar) index { + return index_; +} + +- (size_t) row { + return row_; +} + +- (size_t) count { + return count_; +} + +- (void) addToRow { + ++row_; +} + +- (void) addToCount { + ++count_; +} + +- (void) setCount:(size_t)count { + count_ = count; +} + +- (NSString *) localized { + return localized_; +} + +@end +/* }}} */ + +static NSString *Colon_; +static NSString *Error_; +static NSString *Warning_; + +/* Database Implementation {{{ */ +@implementation Database + ++ (Database *) sharedInstance { + static Database *instance; + if (instance == nil) + instance = [[Database alloc] init]; + return instance; +} + +- (unsigned) era { + return era_; +} + +- (void) dealloc { + _assert(false); + NSRecycleZone(zone_); + // XXX: malloc_destroy_zone(zone_); + apr_pool_destroy(pool_); + [super dealloc]; +} + +- (void) _readCydia:(NSNumber *)fd { _pooled + __gnu_cxx::stdio_filebuf ib([fd intValue], std::ios::in); + std::istream is(&ib); + std::string line; + + static Pcre finish_r("^finish:([^:]*)$"); + + while (std::getline(is, line)) { + const char *data(line.c_str()); + size_t size = line.size(); + lprintf("C:%s\n", data); + + if (finish_r(data, size)) { + NSString *finish = finish_r[1]; + int index = [Finishes_ indexOfObject:finish]; + if (index != INT_MAX && index > Finish_) + Finish_ = index; + } + } + + _assume(false); +} + +- (void) _readStatus:(NSNumber *)fd { _pooled + __gnu_cxx::stdio_filebuf ib([fd intValue], std::ios::in); + std::istream is(&ib); + std::string line; + + static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$"); + static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$"); + + while (std::getline(is, line)) { + const char *data(line.c_str()); + size_t size(line.size()); + lprintf("S:%s\n", data); + + if (conffile_r(data, size)) { + [delegate_ setConfigurationData:conffile_r[1]]; + } else if (strncmp(data, "status: ", 8) == 0) { + NSString *string = [NSString stringWithUTF8String:(data + 8)]; + [delegate_ setProgressTitle:string]; + } else if (pmstatus_r(data, size)) { + std::string type([pmstatus_r[1] UTF8String]); + NSString *id = pmstatus_r[2]; + + float percent([pmstatus_r[3] floatValue]); + [delegate_ setProgressPercent:(percent / 100)]; + + NSString *string = pmstatus_r[4]; + + if (type == "pmerror") + [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:) + withObject:[NSArray arrayWithObjects:string, id, nil] + waitUntilDone:YES + ]; + else if (type == "pmstatus") { + [delegate_ setProgressTitle:string]; + } else if (type == "pmconffile") + [delegate_ setConfigurationData:string]; + else + lprintf("E:unknown pmstatus\n"); + } else + lprintf("E:unknown status\n"); + } + + _assume(false); +} + +- (void) _readOutput:(NSNumber *)fd { _pooled + __gnu_cxx::stdio_filebuf ib([fd intValue], std::ios::in); + std::istream is(&ib); + std::string line; + + while (std::getline(is, line)) { + lprintf("O:%s\n", line.c_str()); + [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]]; + } + + _assume(false); +} + +- (FILE *) input { + return input_; +} + +- (Package *) packageWithName:(NSString *)name { +@synchronized ([Database class]) { + if (static_cast(cache_) == NULL) + return nil; + pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String])); + return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:pool_ database:self]; +} } + +- (Database *) init { + if ((self = [super init]) != nil) { + policy_ = NULL; + records_ = NULL; + resolver_ = NULL; + fetcher_ = NULL; + lock_ = NULL; + + zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO); + apr_pool_create(&pool_, NULL); + + packages_ = [[NSMutableArray alloc] init]; + + int fds[2]; + + _assert(pipe(fds) != -1); + cydiafd_ = fds[1]; + + _config->Set("APT::Keep-Fds::", cydiafd_); + setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int)); + + [NSThread + detachNewThreadSelector:@selector(_readCydia:) + toTarget:self + withObject:[[NSNumber numberWithInt:fds[0]] retain] + ]; + + _assert(pipe(fds) != -1); + statusfd_ = fds[1]; + + [NSThread + detachNewThreadSelector:@selector(_readStatus:) + toTarget:self + withObject:[[NSNumber numberWithInt:fds[0]] retain] + ]; + + _assert(pipe(fds) != -1); + _assert(dup2(fds[0], 0) != -1); + _assert(close(fds[0]) != -1); + + input_ = fdopen(fds[1], "a"); + + _assert(pipe(fds) != -1); + _assert(dup2(fds[1], 1) != -1); + _assert(close(fds[1]) != -1); + + [NSThread + detachNewThreadSelector:@selector(_readOutput:) + toTarget:self + withObject:[[NSNumber numberWithInt:fds[0]] retain] + ]; + } return self; +} + +- (pkgCacheFile &) cache { + return cache_; +} + +- (pkgDepCache::Policy *) policy { + return policy_; +} + +- (pkgRecords *) records { + return records_; +} + +- (pkgProblemResolver *) resolver { + return resolver_; +} + +- (pkgAcquire &) fetcher { + return *fetcher_; +} + +- (pkgSourceList &) list { + return *list_; +} + +- (NSArray *) packages { + return packages_; +} + +- (NSArray *) sources { + NSMutableArray *sources([NSMutableArray arrayWithCapacity:sources_.size()]); + for (SourceMap::const_iterator i(sources_.begin()); i != sources_.end(); ++i) + [sources addObject:i->second]; + return sources; +} + +- (NSArray *) issues { + if (cache_->BrokenCount() == 0) + return nil; + + NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]); + + for (Package *package in packages_) { + if (![package broken]) + continue; + pkgCache::PkgIterator pkg([package iterator]); + + NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]); + [entry addObject:[package name]]; + [issues addObject:entry]; + + pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_)); + if (ver.end()) + continue; + + for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) { + pkgCache::DepIterator start; + pkgCache::DepIterator end; + dep.GlobOr(start, end); // ++dep + + if (!cache_->IsImportantDep(end)) + continue; + if ((cache_[end] & pkgDepCache::DepGInstall) != 0) + continue; + + NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]); + [entry addObject:failure]; + [failure addObject:[NSString stringWithUTF8String:start.DepType()]]; + + NSString *name([NSString stringWithUTF8String:start.TargetPkg().Name()]); + if (Package *package = [self packageWithName:name]) + name = [package name]; + [failure addObject:name]; + + pkgCache::PkgIterator target(start.TargetPkg()); + if (target->ProvidesList != 0) + [failure addObject:@"?"]; + else { + pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_)); + if (!ver.end()) + [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]]; + else if (!cache_[target].CandidateVerIter(cache_).end()) + [failure addObject:@"-"]; + else if (target->ProvidesList == 0) + [failure addObject:@"!"]; + else + [failure addObject:@"%"]; + } + + _forever { + if (start.TargetVer() != 0) + [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]]; + if (start == end) + break; + ++start; + } + } + } + + return issues; +} + +- (bool) popErrorWithTitle:(NSString *)title { + bool fatal(false); + std::string message; + + while (!_error->empty()) { + std::string error; + bool warning(!_error->PopMessage(error)); + if (!warning) + fatal = true; + for (;;) { + size_t size(error.size()); + if (size == 0 || error[size - 1] != '\n') + break; + error.resize(size - 1); + } + lprintf("%c:[%s]\n", warning ? 'W' : 'E', error.c_str()); + + if (!message.empty()) + message += "\n\n"; + message += error; + } + + if (fatal && !message.empty()) + [delegate_ _setProgressError:[NSString stringWithUTF8String:message.c_str()] withTitle:[NSString stringWithFormat:Colon_, fatal ? Error_ : Warning_, title]]; + + return fatal; +} + +- (bool) popErrorWithTitle:(NSString *)title forOperation:(bool)success { + return [self popErrorWithTitle:title] || !success; +} + +- (void) reloadData { _pooled +@synchronized ([Database class]) { + @synchronized (self) { + ++era_; + } + + [packages_ removeAllObjects]; + sources_.clear(); + + _error->Discard(); + + delete list_; + list_ = NULL; + manager_ = NULL; + delete lock_; + lock_ = NULL; + delete fetcher_; + fetcher_ = NULL; + delete resolver_; + resolver_ = NULL; + delete records_; + records_ = NULL; + delete policy_; + policy_ = NULL; + + if (now_ != nil) { + [now_ release]; + now_ = nil; + } + + cache_.Close(); + + apr_pool_clear(pool_); + NSRecycleZone(zone_); + + int chk(creat("/tmp/cydia.chk", 0644)); + if (chk != -1) + close(chk); + + NSString *title(UCLocalize("DATABASE")); + + _trace(); + if (!cache_.Open(progress_, true)) { pop: + std::string error; + bool warning(!_error->PopMessage(error)); + lprintf("cache_.Open():[%s]\n", error.c_str()); + + if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ") + [delegate_ repairWithSelector:@selector(configure)]; + else if (error == "The package lists or status file could not be parsed or opened.") + [delegate_ repairWithSelector:@selector(update)]; + // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)") + // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)") + // else if (error == "The list of sources could not be read.") + else + [delegate_ _setProgressError:[NSString stringWithUTF8String:error.c_str()] withTitle:[NSString stringWithFormat:Colon_, warning ? Warning_ : Error_, title]]; + + if (warning) + goto pop; + _error->Discard(); + return; + } + _trace(); + + unlink("/tmp/cydia.chk"); + + now_ = [[NSDate date] retain]; + + policy_ = new pkgDepCache::Policy(); + records_ = new pkgRecords(cache_); + resolver_ = new pkgProblemResolver(cache_); + fetcher_ = new pkgAcquire(&status_); + lock_ = NULL; + + list_ = new pkgSourceList(); + if ([self popErrorWithTitle:title forOperation:list_->ReadMainList()]) + return; + + if (cache_->DelCount() != 0 || cache_->InstCount() != 0) { + [delegate_ _setProgressError:@"COUNTS_NONZERO_EX" withTitle:title]; + return; + } + + if ([self popErrorWithTitle:title forOperation:pkgApplyStatus(cache_)]) + return; + + if (cache_->BrokenCount() != 0) { + if ([self popErrorWithTitle:title forOperation:pkgFixBroken(cache_)]) + return; + + if (cache_->BrokenCount() != 0) { + [delegate_ _setProgressError:@"STILL_BROKEN_EX" withTitle:title]; + return; + } + + if ([self popErrorWithTitle:title forOperation:pkgMinimizeUpgrade(cache_)]) + return; + } + + _trace(); + + for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) { + std::vector *indices = (*source)->GetIndexFiles(); + for (std::vector::const_iterator index = indices->begin(); index != indices->end(); ++index) + // XXX: this could be more intelligent + if (dynamic_cast(*index) != NULL) { + pkgCache::PkgFileIterator cached((*index)->FindInCache(cache_)); + if (!cached.end()) + sources_[cached->ID] = [[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease]; + } + } + + _trace(); + + { + /*std::vector packages; + packages.reserve(std::max(10000U, [packages_ count] + 1000)); + [packages_ release]; + packages_ = nil;*/ + + _trace(); + + for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator) + if (Package *package = [Package packageWithIterator:iterator withZone:zone_ inPool:pool_ database:self]) + //packages.push_back(package); + [packages_ addObject:package]; + + _trace(); + + /*if (packages.empty()) + packages_ = [[NSArray alloc] init]; + else + packages_ = [[NSArray alloc] initWithObjects:&packages.front() count:packages.size()]; + _trace();*/ + + [packages_ radixSortUsingFunction:reinterpret_cast(&PackagePrefixRadix) withContext:reinterpret_cast(16)]; + [packages_ radixSortUsingFunction:reinterpret_cast(&PackagePrefixRadix) withContext:reinterpret_cast(4)]; + [packages_ radixSortUsingFunction:reinterpret_cast(&PackagePrefixRadix) withContext:reinterpret_cast(0)]; + + /*_trace(); + PrintTimes(); + _trace();*/ + + _trace(); + + /*if (!packages.empty()) + CFQSortArray(&packages.front(), packages.size(), sizeof(packages.front()), reinterpret_cast(&PackageNameCompare_), NULL);*/ + //std::sort(packages.begin(), packages.end(), PackageNameOrdering()); + + //CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast(&PackageNameCompare), NULL); + + CFArrayInsertionSortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast(&PackageNameCompare), NULL); + + //[packages_ sortUsingFunction:reinterpret_cast(&PackageNameCompare) context:NULL]; + + _trace(); + } +} } + +- (void) configure { + NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_]; + system([dpkg UTF8String]); +} + +- (bool) clean { + // XXX: I don't remember this condition + if (lock_ != NULL) + return false; + + FileFd Lock; + Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock")); + + NSString *title(UCLocalize("CLEAN_ARCHIVES")); + + if ([self popErrorWithTitle:title]) + return false; + + pkgAcquire fetcher; + fetcher.Clean(_config->FindDir("Dir::Cache::Archives")); + + class LogCleaner : + public pkgArchiveCleaner + { + protected: + virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) { + unlink(File); + } + } cleaner; + + if ([self popErrorWithTitle:title forOperation:cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)]) + return false; + + return true; +} + +- (bool) prepare { + fetcher_->Shutdown(); + + pkgRecords records(cache_); + + lock_ = new FileFd(); + lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock")); + + NSString *title(UCLocalize("PREPARE_ARCHIVES")); + + if ([self popErrorWithTitle:title]) + return false; + + pkgSourceList list; + if ([self popErrorWithTitle:title forOperation:list.ReadMainList()]) + return false; + + manager_ = (_system->CreatePM(cache_)); + if ([self popErrorWithTitle:title forOperation:manager_->GetArchives(fetcher_, &list, &records)]) + return false; + + return true; +} + +- (void) perform { + NSString *title(UCLocalize("PERFORM_SELECTIONS")); + + NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; { + pkgSourceList list; + if ([self popErrorWithTitle:title forOperation:list.ReadMainList()]) + return; + for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source) + [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]]; + } + + if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) { + _trace(); + return; + } + + bool failed = false; + for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) { + if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete) + continue; + if ((*item)->Status == pkgAcquire::Item::StatIdle) + continue; + + std::string uri = (*item)->DescURI(); + std::string error = (*item)->ErrorText; + + lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str()); + failed = true; + + [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:) + withObject:[NSArray arrayWithObjects: + [NSString stringWithUTF8String:error.c_str()], + nil] + waitUntilDone:YES + ]; + } + + if (failed) { + _trace(); + return; + } + + _system->UnLock(); + pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_); + + if (_error->PendingError()) { + _trace(); + return; + } + + if (result == pkgPackageManager::Failed) { + _trace(); + return; + } + + if (result != pkgPackageManager::Completed) { + _trace(); + return; + } + + NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; { + pkgSourceList list; + if ([self popErrorWithTitle:title forOperation:list.ReadMainList()]) + return; + for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source) + [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]]; + } + + if (![before isEqualToArray:after]) + [self update]; +} + +- (bool) upgrade { + NSString *title(UCLocalize("UPGRADE")); + if ([self popErrorWithTitle:title forOperation:pkgDistUpgrade(cache_)]) + return false; + return true; +} + +- (void) update { + [self updateWithStatus:status_]; +} + +- (void) setVisible { + for (Package *package in packages_) + [package setVisible]; +} + +- (void) updateWithStatus:(Status &)status { + _transient NSObject *delegate(status.getDelegate()); + NSString *title(UCLocalize("REFRESHING_DATA")); + + pkgSourceList list; + if (!list.ReadMainList()) + [delegate _setProgressError:@"Unable to read source list." withTitle:title]; + + FileFd lock; + lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock")); + if ([self popErrorWithTitle:title]) + return; + + if ([self popErrorWithTitle:title forOperation:ListUpdate(status, list, PulseInterval_)]) + /* XXX: ignore this because users suck and don't understand why refreshing is important: return */; + + [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"]; + Changed_ = true; +} + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; + status_.setDelegate(delegate); + progress_.setDelegate(delegate); +} + +- (Source *) getSource:(pkgCache::PkgFileIterator)file { + SourceMap::const_iterator i(sources_.find(file->ID)); + return i == sources_.end() ? nil : i->second; +} + +@end +/* }}} */ + +/* Confirmation View {{{ */ +bool DepSubstrate(const pkgCache::VerIterator &iterator) { + if (!iterator.end()) + for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) { + if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends) + continue; + pkgCache::PkgIterator package(dep.TargetPkg()); + if (package.end()) + continue; + if (strcmp(package.Name(), "mobilesubstrate") == 0) + return true; + } + + return false; +} + +/* Web Scripting {{{ */ +@interface CydiaObject : NSObject { + id indirect_; + id delegate_; +} + +- (id) initWithDelegate:(IndirectDelegate *)indirect; +@end + +@implementation CydiaObject + +- (void) dealloc { + [indirect_ release]; + [super dealloc]; +} + +- (id) initWithDelegate:(IndirectDelegate *)indirect { + if ((self = [super init]) != nil) { + indirect_ = [indirect retain]; + } return self; +} + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects:@"device", @"firewire", @"imei", @"mac", @"serial", nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (NSString *) device { + return [[UIDevice currentDevice] uniqueIdentifier]; +} + +#if 0 // XXX: implement! +- (NSString *) mac { + if (![indirect_ promptForSensitive:@"Mac Address"]) + return nil; +} + +- (NSString *) serial { + if (![indirect_ promptForSensitive:@"Serial #"]) + return nil; +} + +- (NSString *) firewire { + if (![indirect_ promptForSensitive:@"Firewire GUID"]) + return nil; +} + +- (NSString *) imei { + if (![indirect_ promptForSensitive:@"IMEI"]) + return nil; +} +#endif + ++ (NSString *) webScriptNameForSelector:(SEL)selector { + if (selector == @selector(close)) + return @"close"; + else if (selector == @selector(getInstalledPackages)) + return @"getInstalledPackages"; + else if (selector == @selector(getPackageById:)) + return @"getPackageById"; + else if (selector == @selector(installPackages:)) + return @"installPackages"; + else if (selector == @selector(setAutoPopup:)) + return @"setAutoPopup"; + else if (selector == @selector(setButtonImage:withStyle:toFunction:)) + return @"setButtonImage"; + else if (selector == @selector(setButtonTitle:withStyle:toFunction:)) + return @"setButtonTitle"; + else if (selector == @selector(setFinishHook:)) + return @"setFinishHook"; + else if (selector == @selector(setPopupHook:)) + return @"setPopupHook"; + else if (selector == @selector(setSpecial:)) + return @"setSpecial"; + else if (selector == @selector(setToken:)) + return @"setToken"; + else if (selector == @selector(setViewportWidth:)) + return @"setViewportWidth"; + else if (selector == @selector(supports:)) + return @"supports"; + else if (selector == @selector(stringWithFormat:arguments:)) + return @"format"; + else if (selector == @selector(localizedStringForKey:value:table:)) + return @"localize"; + else if (selector == @selector(du:)) + return @"du"; + else if (selector == @selector(statfs:)) + return @"statfs"; + else + return nil; +} + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { + return [self webScriptNameForSelector:selector] == nil; +} + +- (BOOL) supports:(NSString *)feature { + return [feature isEqualToString:@"window.open"]; +} + +- (NSArray *) getInstalledPackages { + NSArray *packages([[Database sharedInstance] packages]); + NSMutableArray *installed([NSMutableArray arrayWithCapacity:[packages count]]); + for (Package *package in packages) + if ([package installed] != nil) + [installed addObject:package]; + return installed; +} + +- (Package *) getPackageById:(NSString *)id { + Package *package([[Database sharedInstance] packageWithName:id]); + [package parse]; + return package; +} + +- (NSArray *) statfs:(NSString *)path { + struct statfs stat; + + if (path == nil || statfs([path UTF8String], &stat) == -1) + return nil; + + return [NSArray arrayWithObjects: + [NSNumber numberWithUnsignedLong:stat.f_bsize], + [NSNumber numberWithUnsignedLong:stat.f_blocks], + [NSNumber numberWithUnsignedLong:stat.f_bfree], + nil]; +} + +- (NSNumber *) du:(NSString *)path { + NSNumber *value(nil); + + int fds[2]; + _assert(pipe(fds) != -1); + + pid_t pid(ExecFork()); + if (pid == 0) { + _assert(dup2(fds[1], 1) != -1); + _assert(close(fds[0]) != -1); + _assert(close(fds[1]) != -1); + /* XXX: this should probably not use du */ + execl("/usr/libexec/cydia/du", "du", "-s", [path UTF8String], NULL); + exit(1); + _assert(false); + } + + _assert(close(fds[1]) != -1); + + if (FILE *du = fdopen(fds[0], "r")) { + 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)]; + } + } + + fclose(du); + } else _assert(close(fds[0])); + + int status; + wait: + if (waitpid(pid, &status, 0) == -1) + if (errno == EINTR) + goto wait; + else _assert(false); + + return value; +} + +- (void) close { + [indirect_ close]; +} + +- (void) installPackages:(NSArray *)packages { + [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO]; +} + +- (void) setAutoPopup:(BOOL)popup { + [indirect_ setAutoPopup:popup]; +} + +- (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { + [indirect_ setButtonImage:button withStyle:style toFunction:function]; +} + +- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { + [indirect_ setButtonTitle:button withStyle:style toFunction:function]; +} + +- (void) setSpecial:(id)function { + [indirect_ setSpecial:function]; +} + +- (void) setToken:(NSString *)token { + if (Token_ != nil) + [Token_ release]; + Token_ = [token retain]; + + [Metadata_ setObject:Token_ forKey:@"Token"]; + Changed_ = true; +} + +- (void) setFinishHook:(id)function { + [indirect_ setFinishHook:function]; +} + +- (void) setPopupHook:(id)function { + [indirect_ setPopupHook:function]; +} + +- (void) setViewportWidth:(float)width { + [indirect_ setViewportWidth:width]; +} + +- (NSString *) stringWithFormat:(NSString *)format arguments:(WebScriptObject *)arguments { + //NSLog(@"SWF:\"%@\" A:%@", format, [arguments description]); + unsigned count([arguments count]); + id values[count]; + for (unsigned i(0); i != count; ++i) + values[i] = [arguments objectAtIndex:i]; + return [[[NSString alloc] initWithFormat:format arguments:reinterpret_cast(values)] autorelease]; +} + +- (NSString *) localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)table { + if (reinterpret_cast(value) == [WebUndefined undefined]) + value = nil; + if (reinterpret_cast(table) == [WebUndefined undefined]) + table = nil; + return [[NSBundle mainBundle] localizedStringForKey:key value:value table:table]; +} + +@end +/* }}} */ + +@interface CydiaBrowserView : BrowserView { + CydiaObject *cydia_; +} + +@end + +@implementation CydiaBrowserView + +- (void) dealloc { + [cydia_ release]; + [super dealloc]; +} + +- (void) setHeaders:(NSDictionary *)headers forHost:(NSString *)host { +} + +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + [super webView:sender didClearWindowObject:window forFrame:frame]; + + WebDataSource *source([frame dataSource]); + NSURLResponse *response([source response]); + NSURL *url([response URL]); + NSString *scheme([url scheme]); + + NSHTTPURLResponse *http; + if (scheme != nil && ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])) + http = (NSHTTPURLResponse *) response; + else + http = nil; + + NSDictionary *headers([http allHeaderFields]); + NSString *host([url host]); + [self setHeaders:headers forHost:host]; + + if ( + [host isEqualToString:@"cydia.saurik.com"] || + [host hasSuffix:@".cydia.saurik.com"] || + [scheme isEqualToString:@"file"] + ) + [window setValue:cydia_ forKey:@"cydia"]; +} + +- (void) _setMoreHeaders:(NSMutableURLRequest *)request { + if (System_ != NULL) + [request setValue:System_ forHTTPHeaderField:@"X-System"]; + if (Machine_ != NULL) + [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; + if (Token_ != nil) + [request setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"]; + if (Role_ != nil) + [request setValue:Role_ forHTTPHeaderField:@"X-Role"]; +} + +- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source { + NSMutableURLRequest *copy = [request mutableCopy]; + [self _setMoreHeaders:copy]; + return copy; +} + +- (void) setDelegate:(id)delegate { + [super setDelegate:delegate]; + [cydia_ setDelegate:delegate]; +} + +- (id) initWithBook:(RVBook *)book forWidth:(float)width { + if ((self = [super initWithBook:book forWidth:width ofClass:[CydiaBrowserView class]]) != nil) { + cydia_ = [[CydiaObject alloc] initWithDelegate:indirect_]; + + WebView *webview([document_ webView]); + + Package *package([[Database sharedInstance] packageWithName:@"cydia"]); + + NSString *application = package == nil ? @"Cydia" : [NSString + stringWithFormat:@"Cydia/%@", + [package installed] + ]; + + if (Safari_ != nil) + application = [NSString stringWithFormat:@"Safari/%@ %@", Safari_, application]; + if (Build_ != nil) + application = [NSString stringWithFormat:@"Mobile/%@ %@", Build_, application]; + if (Product_ != nil) + application = [NSString stringWithFormat:@"Version/%@ %@", Product_, application]; + + [webview setApplicationNameForUserAgent:application]; + } return self; +} + +@end + +@protocol ConfirmationViewDelegate +- (void) cancel; +- (void) confirm; +- (void) queue; +@end + +@interface ConfirmationView : CydiaBrowserView { + _transient Database *database_; + UIActionSheet *essential_; + NSArray *changes_; + NSArray *issues_; + NSArray *sizes_; + BOOL substrate_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; + +@end + +@implementation ConfirmationView + +- (void) dealloc { + [changes_ release]; + if (issues_ != nil) + [issues_ release]; + [sizes_ release]; + if (essential_ != nil) + [essential_ release]; + [super dealloc]; +} + +- (void) cancel { + [delegate_ cancel]; + [book_ popFromSuperviewAnimated:YES]; +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"remove"]) { + switch (button) { + case 1: + [self cancel]; + break; + case 2: + if (substrate_) + Finish_ = 2; + [delegate_ confirm]; + break; + _nodefault + } + + [sheet dismiss]; + } else if ([context isEqualToString:@"unable"]) { + [self cancel]; + [sheet dismiss]; + } else + [super alertSheet:sheet buttonClicked:button]; +} + +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + [super webView:sender didClearWindowObject:window forFrame:frame]; + [window setValue:changes_ forKey:@"changes"]; + [window setValue:issues_ forKey:@"issues"]; + [window setValue:sizes_ forKey:@"sizes"]; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + + NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16]; + NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16]; + NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16]; + NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16]; + NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16]; + + bool remove(false); + + pkgDepCache::Policy *policy([database_ policy]); + + pkgCacheFile &cache([database_ cache]); + NSArray *packages = [database_ packages]; + for (Package *package in packages) { + pkgCache::PkgIterator iterator = [package iterator]; + pkgDepCache::StateCache &state(cache[iterator]); + + NSString *name([package name]); + + if (state.NewInstall()) + [installing addObject:name]; + else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall) + [reinstalling addObject:name]; + else if (state.Upgrade()) + [upgrading addObject:name]; + else if (state.Downgrade()) + [downgrading addObject:name]; + else if (state.Delete()) { + if ([package essential]) + remove = true; + [removing addObject:name]; + } else continue; + + substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator)); + substrate_ |= DepSubstrate(iterator.CurrentVer()); + } + + if (!remove) + essential_ = nil; + else if (Advanced_) { + NSString *parenthetical(UCLocalize("PARENTHETICAL")); + + essential_ = [[UIActionSheet alloc] + initWithTitle:UCLocalize("REMOVING_ESSENTIALS") + buttons:[NSArray arrayWithObjects: + [NSString stringWithFormat:parenthetical, UCLocalize("CANCEL_OPERATION"), UCLocalize("SAFE")], + [NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")], + nil] + defaultButtonIndex:0 + delegate:self + context:@"remove" + ]; + + [essential_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [essential_ setDestructiveButtonIndex:1]; + [essential_ setBodyText:UCLocalize("REMOVING_ESSENTIALS_EX")]; + } else { + essential_ = [[UIActionSheet alloc] + initWithTitle:UCLocalize("UNABLE_TO_COMPLY") + buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil] + defaultButtonIndex:0 + delegate:self + context:@"unable" + ]; + + [essential_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [essential_ setBodyText:UCLocalize("UNABLE_TO_COMPLY_EX")]; + } + + changes_ = [[NSArray alloc] initWithObjects: + installing, + reinstalling, + upgrading, + downgrading, + removing, + nil]; + + issues_ = [database_ issues]; + if (issues_ != nil) + issues_ = [issues_ retain]; + + sizes_ = [[NSArray alloc] initWithObjects: + SizeString([database_ fetcher].FetchNeeded()), + SizeString([database_ fetcher].PartialPresent()), + SizeString([database_ cache]->UsrSize()), + nil]; + + [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]]; + + [self setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + } return self; +} + +- (NSString *) backButtonTitle { + return UCLocalize("CONFIRM"); +} + +- (NSString *) leftButtonTitle { + return [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("CANCEL"), UCLocalize("QUEUE")]; +} + +- (id) rightButtonTitle { + return issues_ != nil ? nil : [super rightButtonTitle]; +} + +- (id) _rightButtonTitle { +#if AlwaysReload || IgnoreInstall + return [super _rightButtonTitle]; +#else + return UCLocalize("CONFIRM"); +#endif +} + +- (void) _leftButtonClicked { + [self cancel]; +} + +#if !AlwaysReload +- (void) _rightButtonClicked { +#if IgnoreInstall + return [super _rightButtonClicked]; +#endif + if (essential_ != nil) + [essential_ popupAlertAnimated:YES]; + else { + if (substrate_) + Finish_ = 2; + [delegate_ confirm]; + } +} +#endif + +@end +/* }}} */ + +/* Progress Data {{{ */ +@interface ProgressData : NSObject { + SEL selector_; + id target_; + id object_; +} + +- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object; + +- (SEL) selector; +- (id) target; +- (id) object; +@end + +@implementation ProgressData + +- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object { + if ((self = [super init]) != nil) { + selector_ = selector; + target_ = target; + object_ = object; + } return self; +} + +- (SEL) selector { + return selector_; +} + +- (id) target { + return target_; +} + +- (id) object { + return object_; +} + +@end +/* }}} */ +/* Progress View {{{ */ +@interface ProgressView : UIView < + ConfigurationDelegate, + ProgressDelegate +> { + _transient Database *database_; + UIView *view_; + UIView *background_; + UITransitionView *transition_; + UIView *overlay_; + UINavigationBar *navbar_; + UIProgressBar *progress_; + UITextView *output_; + UITextLabel *status_; + UIPushButton *close_; + id delegate_; + BOOL running_; + SHA1SumValue springlist_; + SHA1SumValue notifyconf_; + NSString *title_; +} + +- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate; +- (void) setContentView:(UIView *)view; +- (void) resetView; + +- (void) _retachThread; +- (void) _detachNewThreadData:(ProgressData *)data; +- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title; + +- (BOOL) isRunning; + +@end + +@protocol ProgressViewDelegate +- (void) progressViewIsComplete:(ProgressView *)sender; +@end + +@implementation ProgressView + +- (void) dealloc { + [transition_ setDelegate:nil]; + [navbar_ setDelegate:nil]; + + [view_ release]; + if (background_ != nil) + [background_ release]; + [transition_ release]; + [overlay_ release]; + [navbar_ release]; + [progress_ release]; + [output_ release]; + [status_ release]; + [close_ release]; + if (title_ != nil) + [title_ release]; + [super dealloc]; +} + +- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate { + if ((self = [super initWithFrame:frame]) != nil) { + database_ = database; + delegate_ = delegate; + + transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]]; + [transition_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [transition_ setDelegate:self]; + + overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]]; + [overlay_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + + background_ = [[UIView alloc] initWithFrame:[self bounds]]; + [background_ setBackgroundColor:[UIColor blackColor]]; + [self addSubview:background_]; + + [self addSubview:transition_]; + + CGSize navsize = [UINavigationBar defaultSize]; + CGRect navrect = {{0, 0}, navsize}; + + navbar_ = [[UINavigationBar alloc] initWithFrame:navrect]; + [navbar_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [overlay_ addSubview:navbar_]; + + [navbar_ setBarStyle:1]; + [navbar_ setDelegate:self]; + + UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease]; + [navbar_ pushNavigationItem:navitem]; + + CGRect bounds = [overlay_ bounds]; + CGSize prgsize = [UIProgressBar defaultSize]; + + CGRect prgrect = {{ + (bounds.size.width - prgsize.width) / 2, + bounds.size.height - prgsize.height - 20 + }, prgsize}; + + progress_ = [[UIProgressBar alloc] initWithFrame:prgrect]; + [progress_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; + [progress_ setStyle:0]; + + status_ = [[UITextLabel alloc] initWithFrame:CGRectMake( + 10, + bounds.size.height - prgsize.height - 50, + bounds.size.width - 20, + 24 + )]; + + [status_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; + + [status_ setColor:[UIColor whiteColor]]; + [status_ setBackgroundColor:[UIColor clearColor]]; + + [status_ setCentersHorizontally:YES]; + //[status_ setFont:font]; + + output_ = [[UITextView alloc] initWithFrame:CGRectMake( + 10, + navrect.size.height + 20, + bounds.size.width - 20, + bounds.size.height - navsize.height - 62 - navrect.size.height + )]; + + [output_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [overlay_ addSubview:output_]; + + //[output_ setTextFont:@"Courier New"]; + [output_ setFont:[[output_ font] fontWithSize:12]]; + + [output_ setTextColor:[UIColor whiteColor]]; + [output_ setBackgroundColor:[UIColor clearColor]]; + + [output_ setMarginTop:0]; + [output_ setAllowsRubberBanding:YES]; + [output_ setEditable:NO]; + + close_ = [[UIPushButton alloc] initWithFrame:CGRectMake( + 10, + bounds.size.height - prgsize.height - 50, + bounds.size.width - 20, + 32 + prgsize.height + )]; + + [close_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; + + [close_ setAutosizesToFit:NO]; + [close_ setDrawsShadow:YES]; + [close_ setStretchBackground:YES]; + [close_ setEnabled:YES]; + + UIFont *bold = [UIFont boldSystemFontOfSize:22]; + [close_ setTitleFont:bold]; + + [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:UIControlEventTouchUpInside]; + [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0]; + [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1]; + } return self; +} + +- (void) setContentView:(UIView *)view { + view_ = [view retain]; +} + +- (void) resetView { + [transition_ transition:6 toView:view_]; +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"conffile"]) { + FILE *input = [database_ input]; + + switch (button) { + case 1: + fprintf(input, "N\n"); + fflush(input); + break; + case 2: + fprintf(input, "Y\n"); + fflush(input); + break; + _nodefault + } + + [sheet dismiss]; + } +} + +- (void) closeButtonPushed { + running_ = NO; + + switch (Finish_) { + case 0: + [self resetView]; + break; + + case 1: + [delegate_ terminateWithSuccess]; + /*if ([delegate_ respondsToSelector:@selector(suspendWithAnimation:)]) + [delegate_ suspendWithAnimation:YES]; + else + [delegate_ suspend];*/ + break; + + case 2: + system("launchctl stop com.apple.SpringBoard"); + break; + + case 3: + system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_); + break; + + case 4: + system("reboot"); + break; + } +} + +- (void) _retachThread { + UINavigationItem *item([navbar_ topItem]); + [item setTitle:UCLocalize("COMPLETE")]; + + [overlay_ addSubview:close_]; + [progress_ removeFromSuperview]; + [status_ removeFromSuperview]; + + [database_ popErrorWithTitle:title_]; + [delegate_ progressViewIsComplete:self]; + + if (Finish_ < 4) { + FileFd file; + if (!file.Open(NotifyConfig_, FileFd::ReadOnly)) + _error->Discard(); + else { + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); + if (!(notifyconf_ == sha1.Result())) + Finish_ = 4; + } + } + + if (Finish_ < 3) { + FileFd file; + if (!file.Open(SpringBoard_, FileFd::ReadOnly)) + _error->Discard(); + else { + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); + if (!(springlist_ == sha1.Result())) + Finish_ = 3; + } + } + + switch (Finish_) { + case 0: [close_ setTitle:UCLocalize("RETURN_TO_CYDIA")]; break; + case 1: [close_ setTitle:UCLocalize("CLOSE_CYDIA")]; break; + case 2: [close_ setTitle:UCLocalize("RESTART_SPRINGBOARD")]; break; + case 3: [close_ setTitle:UCLocalize("RELOAD_SPRINGBOARD")]; break; + case 4: [close_ setTitle:UCLocalize("REBOOT_DEVICE")]; break; + } + + system("su -c /usr/bin/uicache mobile"); + + [delegate_ setStatusBarShowsProgress:NO]; +} + +- (void) _detachNewThreadData:(ProgressData *)data { _pooled + [[data target] performSelector:[data selector] withObject:[data object]]; + [data release]; + + [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES]; +} + +- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title { + if (title_ != nil) + [title_ release]; + if (title == nil) + title_ = nil; + else + title_ = [title retain]; + + UINavigationItem *item([navbar_ topItem]); + [item setTitle:title_]; + + [status_ setText:nil]; + [output_ setText:@""]; + [progress_ setProgress:0]; + + [close_ removeFromSuperview]; + [overlay_ addSubview:progress_]; + [overlay_ addSubview:status_]; + + [delegate_ setStatusBarShowsProgress:YES]; + running_ = YES; + + { + FileFd file; + if (!file.Open(NotifyConfig_, FileFd::ReadOnly)) + _error->Discard(); + else { + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); + notifyconf_ = sha1.Result(); + } + } + + { + FileFd file; + if (!file.Open(SpringBoard_, FileFd::ReadOnly)) + _error->Discard(); + else { + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); + springlist_ = sha1.Result(); + } + } + + [transition_ transition:6 toView:overlay_]; + + [NSThread + detachNewThreadSelector:@selector(_detachNewThreadData:) + toTarget:self + withObject:[[ProgressData alloc] + initWithSelector:selector + target:target + object:object + ] + ]; +} + +- (void) repairWithSelector:(SEL)selector { + [self + detachNewThreadSelector:selector + toTarget:database_ + withObject:nil + title:UCLocalize("REPAIRING") + ]; +} + +- (void) setConfigurationData:(NSString *)data { + [self + performSelectorOnMainThread:@selector(_setConfigurationData:) + withObject:data + waitUntilDone:YES + ]; +} + +- (void) setProgressError:(NSString *)error withTitle:(NSString *)title { + CYActionSheet *sheet([[[CYActionSheet alloc] + initWithTitle:title + buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil] + defaultButtonIndex:0 + ] autorelease]); + + [sheet setBodyText:error]; + [sheet yieldToPopupAlertAnimated:YES]; + [sheet dismiss]; +} + +- (void) setProgressTitle:(NSString *)title { + [self + performSelectorOnMainThread:@selector(_setProgressTitle:) + withObject:title + waitUntilDone:YES + ]; +} + +- (void) setProgressPercent:(float)percent { + [self + performSelectorOnMainThread:@selector(_setProgressPercent:) + withObject:[NSNumber numberWithFloat:percent] + waitUntilDone:YES + ]; +} + +- (void) startProgress { +} + +- (void) addProgressOutput:(NSString *)output { + [self + performSelectorOnMainThread:@selector(_addProgressOutput:) + withObject:output + waitUntilDone:YES + ]; +} + +- (bool) isCancelling:(size_t)received { + return false; +} + +- (void) _setConfigurationData:(NSString *)data { + static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$"); + + if (!conffile_r(data)) { + lprintf("E:invalid conffile\n"); + return; + } + + NSString *ofile = conffile_r[1]; + //NSString *nfile = conffile_r[2]; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:UCLocalize("CONFIGURATION_UPGRADE") + buttons:[NSArray arrayWithObjects: + UCLocalize("KEEP_OLD_COPY"), + UCLocalize("ACCEPT_NEW_COPY"), + // XXX: UCLocalize("SEE_WHAT_CHANGED"), + nil] + defaultButtonIndex:0 + delegate:self + context:@"conffile" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setBodyText:[NSString stringWithFormat:@"%@\n\n%@", UCLocalize("CONFIGURATION_UPGRADE_EX"), ofile]]; + [sheet popupAlertAnimated:YES]; +} + +- (void) _setProgressTitle:(NSString *)title { + NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]); + for (size_t i(0), e([words count]); i != e; ++i) { + NSString *word([words objectAtIndex:i]); + if (Package *package = [database_ packageWithName:word]) + [words replaceObjectAtIndex:i withObject:[package name]]; + } + + [status_ setText:[words componentsJoinedByString:@" "]]; +} + +- (void) _setProgressPercent:(NSNumber *)percent { + [progress_ setProgress:[percent floatValue]]; +} + +- (void) _addProgressOutput:(NSString *)output { + [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]]; + CGSize size = [output_ contentSize]; + CGRect rect = {{0, size.height}, {size.width, 0}}; + [output_ scrollRectToVisible:rect animated:YES]; +} + +- (BOOL) isRunning { + return running_; +} + +@end +/* }}} */ + +/* Package Cell {{{ */ +@interface ContentView : UIView { + _transient id delegate_; +} + +@end + +@interface PackageCell : UITableViewCell { + UIImage *icon_; + NSString *name_; + NSString *description_; + bool commercial_; + NSString *source_; + UIImage *badge_; + Package *package_; + UIColor *color_; + ContentView *content_; + BOOL faded_; + float fade_; + UIImage *placard_; +} + +- (PackageCell *) init; +- (void) setPackage:(Package *)package; + ++ (int) heightForPackage:(Package *)package; +- (void) drawContentRect:(CGRect)rect; + +@end + +@implementation ContentView + +- (id) initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame]) != nil) { + } return self; +} + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + +- (void) drawRect:(CGRect)rect { + [super drawRect:rect]; + [delegate_ drawContentRect:rect]; +} + +@end + +@implementation PackageCell + +- (void) clearPackage { + if (icon_ != nil) { + [icon_ release]; + icon_ = nil; + } + + if (name_ != nil) { + [name_ release]; + name_ = nil; + } + + if (description_ != nil) { + [description_ release]; + description_ = nil; + } + + if (source_ != nil) { + [source_ release]; + source_ = nil; + } + + if (badge_ != nil) { + [badge_ release]; + badge_ = nil; + } + + if (placard_ != nil) { + [placard_ release]; + placard_ = nil; + } + + [package_ release]; + package_ = nil; +} + +- (void) dealloc { + [self clearPackage]; + [content_ release]; + [color_ release]; + [super dealloc]; +} + +- (float) fade { + return faded_ ? [self selectionPercent] : fade_; +} + +- (PackageCell *) init { + CGRect frame(CGRectMake(0, 0, 320, 74)); + if ((self = [super initWithFrame:frame reuseIdentifier:@"Package"]) != nil) { + UIView *content([self contentView]); + CGRect bounds([content bounds]); + + content_ = [[ContentView alloc] initWithFrame:bounds]; + [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [content addSubview:content_]; + + [content_ setDelegate:self]; + [content_ setOpaque:YES]; + if ([self respondsToSelector:@selector(selectionPercent)]) + faded_ = YES; + + [self setNeedsDisplayOnBoundsChange:YES]; + } return self; +} + +- (void) _setBackgroundColor { + UIColor *color; + if (NSString *mode = [package_ mode]) { + bool remove([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]); + color = remove ? RemovingColor_ : InstallingColor_; + } else + color = [UIColor whiteColor]; + + [content_ setBackgroundColor:color]; + [self setNeedsDisplay]; +} + +- (void) setPackage:(Package *)package { + [self clearPackage]; + [package parse]; + + Source *source = [package source]; + + icon_ = [[package icon] retain]; + name_ = [[package name] retain]; + + if (IsWildcat_) + description_ = [package longDescription]; + if (description_ == nil) + description_ = [package shortDescription]; + if (description_ != nil) + description_ = [description_ retain]; + + commercial_ = [package isCommercial]; + + package_ = [package retain]; + + NSString *label = nil; + bool trusted = false; + + if (source != nil) { + label = [source label]; + trusted = [source trusted]; + } else if ([[package id] isEqualToString:@"firmware"]) + label = UCLocalize("APPLE"); + else + label = [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("UNKNOWN"), UCLocalize("LOCAL")]; + + NSString *from(label); + + NSString *section = [package simpleSection]; + if (section != nil && ![section isEqualToString:label]) { + section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"]; + from = [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), from, section]; + } + + from = [NSString stringWithFormat:UCLocalize("FROM"), from]; + source_ = [from retain]; + + if (NSString *purpose = [package primaryPurpose]) + if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil) + badge_ = [badge_ retain]; + + if ([package installed] != nil) + if ((placard_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/installed.png", App_]]) != nil) + placard_ = [placard_ retain]; + + [self _setBackgroundColor]; + [content_ setNeedsDisplay]; +} + +- (void) drawContentRect:(CGRect)rect { + bool selected([self isSelected]); + float width([self bounds].size.width); + +#if 0 + CGContextRef context(UIGraphicsGetCurrentContext()); + [([[self selectedBackgroundView] superview] != nil ? [UIColor clearColor] : [self backgroundColor]) set]; + CGContextFillRect(context, rect); +#endif + + if (icon_ != nil) { + CGRect rect; + rect.size = [icon_ size]; + + rect.size.width /= 2; + rect.size.height /= 2; + + rect.origin.x = 25 - rect.size.width / 2; + rect.origin.y = 25 - rect.size.height / 2; + + [icon_ drawInRect:rect]; + } + + if (badge_ != nil) { + CGSize size = [badge_ size]; + + [badge_ drawAtPoint:CGPointMake( + 36 - size.width / 2, + 36 - size.height / 2 + )]; + } + + if (selected) + UISetColor(White_); + + if (!selected) + UISetColor(commercial_ ? Purple_ : Black_); + [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - (placard_ == nil ? 80 : 106)) withFont:Font18Bold_ ellipsis:2]; + [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ ellipsis:2]; + + if (!selected) + UISetColor(commercial_ ? Purplish_ : Gray_); + [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ ellipsis:2]; + + if (placard_ != nil) + [placard_ drawAtPoint:CGPointMake(width - 52, 9)]; +} + +- (void) setSelected:(BOOL)selected animated:(BOOL)fade { + //[self _setBackgroundColor]; + [super setSelected:selected animated:fade]; + [content_ setNeedsDisplay]; +} + ++ (int) heightForPackage:(Package *)package { + return 73; +} + +@end +/* }}} */ +/* Section Cell {{{ */ +@interface SectionCell : UISimpleTableCell { + NSString *basic_; + NSString *section_; + NSString *name_; + NSString *count_; + UIImage *icon_; + _UISwitchSlider *switch_; + BOOL editing_; +} + +- (id) init; +- (void) setSection:(Section *)section editing:(BOOL)editing; + +@end + +@implementation SectionCell + +- (void) clearSection { + if (basic_ != nil) { + [basic_ release]; + basic_ = nil; + } + + if (section_ != nil) { + [section_ release]; + section_ = nil; + } + + if (name_ != nil) { + [name_ release]; + name_ = nil; + } + + if (count_ != nil) { + [count_ release]; + count_ = nil; + } +} + +- (void) dealloc { + [self clearSection]; + [icon_ release]; + [switch_ release]; + [super dealloc]; +} + +- (id) init { + if ((self = [super init]) != nil) { + icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain]; + switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)]; + [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventTouchUpInside]; + } return self; +} + +- (void) onSwitch:(id)sender { + NSMutableDictionary *metadata = [Sections_ objectForKey:basic_]; + if (metadata == nil) { + metadata = [NSMutableDictionary dictionaryWithCapacity:2]; + [Sections_ setObject:metadata forKey:basic_]; + } + + Changed_ = true; + [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"]; +} + +- (void) setSection:(Section *)section editing:(BOOL)editing { + if (editing != editing_) { + if (editing_) + [switch_ removeFromSuperview]; + else + [self addSubview:switch_]; + editing_ = editing; + } + + [self clearSection]; + + if (section == nil) { + name_ = [UCLocalize("ALL_PACKAGES") retain]; + count_ = nil; + } else { + basic_ = [section name]; + if (basic_ != nil) + basic_ = [basic_ retain]; + + section_ = [section localized]; + if (section_ != nil) + section_ = [section_ retain]; + + name_ = [(section_ == nil || [section_ length] == 0 ? UCLocalize("NO_SECTION") : section_) retain]; + count_ = [[NSString stringWithFormat:@"%d", [section count]] retain]; + + if (editing_) + [switch_ setValue:(isSectionVisible(basic_) ? 1 : 0) animated:NO]; + } +} + +- (void) setFrame:(CGRect)frame { + [super setFrame:frame]; + CGRect rect([switch_ frame]); + [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)]; +} + +- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected { + [icon_ drawInRect:CGRectMake(8, 7, 32, 32)]; + + if (selected) + UISetColor(White_); + + if (!selected) + UISetColor(Black_); + + float width(rect.size.width + 23); + if (editing_) + width -= 110; + + [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(width - 70) withFont:Font22Bold_ ellipsis:2]; + + CGSize size = [count_ sizeWithFont:Font14_]; + + UISetColor(White_); + if (count_ != nil) + [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_]; + + [super drawContentInRect:rect selected:selected]; +} + +@end +/* }}} */ + +/* File Table {{{ */ +@interface FileTable : RVPage { + _transient Database *database_; + Package *package_; + NSString *name_; + NSMutableArray *files_; + UITable *list_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; +- (void) setPackage:(Package *)package; + +@end + +@implementation FileTable + +- (void) dealloc { + if (package_ != nil) + [package_ release]; + if (name_ != nil) + [name_ release]; + [files_ release]; + [list_ release]; + [super dealloc]; +} + +- (int) numberOfRowsInTable:(UITable *)table { + return files_ == nil ? 0 : [files_ count]; +} + +- (float) table:(UITable *)table heightForRow:(int)row { + return 24; +} + +- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing { + if (reusing == nil) { + reusing = [[[UIImageAndTextTableCell alloc] init] autorelease]; + UIFont *font = [UIFont systemFontOfSize:16]; + [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font]; + } + [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]]; + return reusing; +} + +- (BOOL) table:(UITable *)table canSelectRow:(int)row { + return NO; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + + files_ = [[NSMutableArray arrayWithCapacity:32] retain]; + + list_ = [[UITable alloc] initWithFrame:[self bounds]]; + [self addSubview:list_]; + + UITableColumn *column = [[[UITableColumn alloc] + initWithTitle:UCLocalize("NAME") + identifier:@"name" + width:[self frame].size.width + ] autorelease]; + + [list_ setDataSource:self]; + [list_ setSeparatorStyle:1]; + [list_ addTableColumn:column]; + [list_ setDelegate:self]; + [list_ setReusesTableCells:YES]; + } return self; +} + +- (void) setPackage:(Package *)package { + if (package_ != nil) { + [package_ autorelease]; + package_ = nil; + } + + if (name_ != nil) { + [name_ release]; + name_ = nil; + } + + [files_ removeAllObjects]; + + if (package != nil) { + package_ = [package retain]; + name_ = [[package id] retain]; + + if (NSArray *files = [package files]) + [files_ addObjectsFromArray:files]; + + if ([files_ count] != 0) { + if ([[files_ objectAtIndex:0] isEqualToString:@"/."]) + [files_ removeObjectAtIndex:0]; + [files_ sortUsingSelector:@selector(compareByPath:)]; + + NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8]; + [stack addObject:@"/"]; + + for (int i(0), e([files_ count]); i != e; ++i) { + NSString *file = [files_ objectAtIndex:i]; + while (![file hasPrefix:[stack lastObject]]) + [stack removeLastObject]; + NSString *directory = [stack lastObject]; + [stack addObject:[file stringByAppendingString:@"/"]]; + [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@", + ([stack count] - 2) * 3, "", + [file substringFromIndex:[directory length]] + ]]; + } + } + } + + [list_ reloadData]; +} + +- (void) resetViewAnimated:(BOOL)animated { + [list_ resetViewAnimated:animated]; +} + +- (void) reloadData { + [self setPackage:[database_ packageWithName:name_]]; + [self reloadButtons]; +} + +- (NSString *) title { + return UCLocalize("INSTALLED_FILES"); +} + +- (NSString *) backButtonTitle { + return UCLocalize("FILES"); +} + +@end +/* }}} */ +/* Package View {{{ */ +@interface PackageView : CydiaBrowserView { + _transient Database *database_; + Package *package_; + NSString *name_; + bool commercial_; + NSMutableArray *buttons_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; +- (void) setPackage:(Package *)package; + +@end + +@implementation PackageView + +- (void) dealloc { + if (package_ != nil) + [package_ release]; + if (name_ != nil) + [name_ release]; + [buttons_ release]; + [super dealloc]; +} + +- (void) release { + if ([self retainCount] == 1) + [delegate_ setPackageView:self]; + [super release]; +} + +/* XXX: this is not safe at all... localization of /fail/ */ +- (void) _clickButtonWithName:(NSString *)name { + if ([name isEqualToString:UCLocalize("CLEAR")]) + [delegate_ clearPackage:package_]; + else if ([name isEqualToString:UCLocalize("INSTALL")]) + [delegate_ installPackage:package_]; + else if ([name isEqualToString:UCLocalize("REINSTALL")]) + [delegate_ installPackage:package_]; + else if ([name isEqualToString:UCLocalize("REMOVE")]) + [delegate_ removePackage:package_]; + else if ([name isEqualToString:UCLocalize("UPGRADE")]) + [delegate_ installPackage:package_]; + else _assert(false); +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"modify"]) { + int count = [buttons_ count]; + _assert(count != 0); + _assert(button <= count + 1); + + if (count != button - 1) + [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]]; + + [sheet dismiss]; + } else + [super alertSheet:sheet buttonClicked:button]; +} + +- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { + return [super webView:sender didFinishLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + [super webView:sender didClearWindowObject:window forFrame:frame]; + [window setValue:package_ forKey:@"package"]; +} + +- (bool) _allowJavaScriptPanel { + return commercial_; +} + +#if !AlwaysReload +- (void) __rightButtonClicked { + int count([buttons_ count]); + if (count == 0) + return; + + if (count == 1) + [self _clickButtonWithName:[buttons_ objectAtIndex:0]]; + else { + NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)]; + [buttons addObjectsFromArray:buttons_]; + [buttons addObject:UCLocalize("CANCEL")]; + + [delegate_ slideUp:[[[UIActionSheet alloc] + initWithTitle:nil + buttons:buttons + defaultButtonIndex:([buttons count] - 1) + delegate:self + context:@"modify" + ] autorelease]]; + } +} + +- (void) _rightButtonClicked { + if (commercial_) + [super _rightButtonClicked]; + else + [self __rightButtonClicked]; +} +#endif + +- (id) _rightButtonTitle { + int count = [buttons_ count]; + return count == 0 ? nil : count != 1 ? UCLocalize("MODIFY") : [buttons_ objectAtIndex:0]; +} + +- (NSString *) backButtonTitle { + return @"Details"; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + buttons_ = [[NSMutableArray alloc] initWithCapacity:4]; + [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]]; + } return self; +} + +- (void) setPackage:(Package *)package { + if (package_ != nil) { + [package_ autorelease]; + package_ = nil; + } + + if (name_ != nil) { + [name_ release]; + name_ = nil; + } + + [buttons_ removeAllObjects]; + + if (package != nil) { + [package parse]; + + package_ = [package retain]; + name_ = [[package id] retain]; + commercial_ = [package isCommercial]; + + if ([package_ mode] != nil) + [buttons_ addObject:UCLocalize("CLEAR")]; + if ([package_ source] == nil); + else if ([package_ upgradableAndEssential:NO]) + [buttons_ addObject:UCLocalize("UPGRADE")]; + else if ([package_ uninstalled]) + [buttons_ addObject:UCLocalize("INSTALL")]; + else + [buttons_ addObject:UCLocalize("REINSTALL")]; + if (![package_ uninstalled]) + [buttons_ addObject:UCLocalize("REMOVE")]; + + if (special_ != NULL) { + CGRect frame([document_ frame]); + frame.size.width = 320; + frame.size.height = 0; + [document_ setFrame:frame]; + + if ([scroller_ respondsToSelector:@selector(scrollPointVisibleAtTopLeft:)]) + [scroller_ scrollPointVisibleAtTopLeft:CGPointZero]; + else + [scroller_ scrollRectToVisible:CGRectZero animated:NO]; + + WebThreadLock(); + [[[document_ webView] windowScriptObject] setValue:package_ forKey:@"package"]; + + [self setButtonTitle:nil withStyle:nil toFunction:nil]; + + [self setFinishHook:nil]; + [self setPopupHook:nil]; + WebThreadUnlock(); + + //[self yieldToSelector:@selector(callFunction:) withObject:special_]; + [super callFunction:special_]; + } + } + + [self reloadButtons]; +} + +- (bool) isLoading { + return commercial_ ? [super isLoading] : false; +} + +- (void) reloadData { + [self setPackage:[database_ packageWithName:name_]]; +} + +@end +/* }}} */ +/* Package Table {{{ */ +@interface PackageTable : RVPage { + _transient Database *database_; + NSString *title_; + NSMutableArray *packages_; + NSMutableArray *sections_; + UITableView *list_; + NSMutableArray *index_; + NSMutableDictionary *indices_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title; + +- (void) setDelegate:(id)delegate; + +- (void) reloadData; +- (void) resetCursor; + +- (UITableView *) list; + +- (void) setShouldHideHeaderInShortLists:(BOOL)hide; + +@end + +@implementation PackageTable + +- (void) dealloc { + [list_ setDataSource:nil]; + + [title_ release]; + [packages_ release]; + [sections_ release]; + [list_ release]; + [index_ release]; + [indices_ release]; + [super dealloc]; +} + +- (NSInteger) numberOfSectionsInTableView:(UITableView *)list { + NSInteger count([sections_ count]); + return count == 0 ? 1 : count; +} + +- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section { + if ([sections_ count] == 0) + return nil; + return [[sections_ objectAtIndex:section] name]; +} + +- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section { + if ([sections_ count] == 0) + return 0; + return [[sections_ objectAtIndex:section] count]; +} + +- (Package *) packageAtIndexPath:(NSIndexPath *)path { + Section *section([sections_ objectAtIndex:[path section]]); + NSInteger row([path row]); + Package *package([packages_ objectAtIndex:([section row] + row)]); + return package; +} + +- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path { + PackageCell *cell([table dequeueReusableCellWithIdentifier:@"Package"]); + if (cell == nil) + cell = [[[PackageCell alloc] init] autorelease]; + [cell setPackage:[self packageAtIndexPath:path]]; + return cell; +} + +- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path { + return 73; + return [PackageCell heightForPackage:[self packageAtIndexPath:path]]; +} + +- (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path { + Package *package([self packageAtIndexPath:path]); + package = [database_ packageWithName:[package id]]; + PackageView *view([delegate_ packageView]); + [view setPackage:package]; + [view setDelegate:delegate_]; + [book_ pushPage:view]; + return path; +} + +- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView { + return [packages_ count] > 20 ? index_ : nil; +} + +- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { + return index; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + title_ = [title retain]; + + index_ = [[NSMutableArray alloc] initWithCapacity:32]; + indices_ = [[NSMutableDictionary alloc] initWithCapacity:32]; + + packages_ = [[NSMutableArray arrayWithCapacity:16] retain]; + sections_ = [[NSMutableArray arrayWithCapacity:16] retain]; + + list_ = [[UITableView alloc] initWithFrame:[self bounds] style:UITableViewStylePlain]; + [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [self addSubview:list_]; + + [list_ setDataSource:self]; + [list_ setDelegate:self]; + + [self setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + } return self; +} + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + +- (bool) hasPackage:(Package *)package { + return true; +} + +- (void) reloadData { + NSArray *packages = [database_ packages]; + + [packages_ removeAllObjects]; + [sections_ removeAllObjects]; + + _profile(PackageTable$reloadData$Filter) + for (Package *package in packages) + if ([self hasPackage:package]) + [packages_ addObject:package]; + _end + + [index_ removeAllObjects]; + [indices_ removeAllObjects]; + + Section *section = nil; + + _profile(PackageTable$reloadData$Section) + for (size_t offset(0), end([packages_ count]); offset != end; ++offset) { + Package *package; + unichar index; + + _profile(PackageTable$reloadData$Section$Package) + package = [packages_ objectAtIndex:offset]; + index = [package index]; + _end + + if (section == nil || [section index] != index) { + _profile(PackageTable$reloadData$Section$Allocate) + section = [[[Section alloc] initWithIndex:index row:offset] autorelease]; + _end + + [index_ addObject:[section name]]; + //[indices_ setObject:[NSNumber numberForInt:[sections_ count]] forKey:index]; + + _profile(PackageTable$reloadData$Section$Add) + [sections_ addObject:section]; + _end + } + + [section addToCount]; + } + _end + + _profile(PackageTable$reloadData$List) + [list_ reloadData]; + _end +} + +- (NSString *) title { + return title_; +} + +- (void) resetViewAnimated:(BOOL)animated { + [list_ resetViewAnimated:animated]; +} + +- (void) resetCursor { + [list_ scrollRectToVisible:CGRectMake(0, 0, 0, 0) animated:NO]; +} + +- (UITableView *) list { + return list_; +} + +- (void) setShouldHideHeaderInShortLists:(BOOL)hide { + //XXX:[list_ setShouldHideHeaderInShortLists:hide]; +} + +@end +/* }}} */ +/* Filtered Package Table {{{ */ +@interface FilteredPackageTable : PackageTable { + SEL filter_; + IMP imp_; + id object_; +} + +- (void) setObject:(id)object; +- (void) setObject:(id)object forFilter:(SEL)filter; + +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object; + +@end + +@implementation FilteredPackageTable + +- (void) dealloc { + if (object_ != nil) + [object_ release]; + [super dealloc]; +} + +- (void) setFilter:(SEL)filter { + filter_ = filter; + + /* XXX: this is an unsafe optimization of doomy hell */ + Method method(class_getInstanceMethod([Package class], filter)); + _assert(method != NULL); + imp_ = method_getImplementation(method); + _assert(imp_ != NULL); +} + +- (void) setObject:(id)object { + if (object_ != nil) + [object_ release]; + if (object == nil) + object_ = nil; + else + object_ = [object retain]; +} + +- (void) setObject:(id)object forFilter:(SEL)filter { + [self setFilter:filter]; + [self setObject:object]; + +} + +- (bool) hasPackage:(Package *)package { + _profile(FilteredPackageTable$hasPackage) + return [package valid] && (*reinterpret_cast(imp_))(package, filter_, object_); + _end +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object { + if ((self = [super initWithBook:book database:database title:title]) != nil) { + [self setFilter:filter]; + object_ = object == nil ? nil : [object retain]; + [self reloadData]; + } return self; +} + +@end +/* }}} */ + +/* Add Source View {{{ */ +@interface AddSourceView : RVPage { + _transient Database *database_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; + +@end + +@implementation AddSourceView + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + } return self; +} + +@end +/* }}} */ +/* Source Cell {{{ */ +@interface SourceCell : UITableCell { + UIImage *icon_; + NSString *origin_; + NSString *description_; + NSString *label_; +} + +- (void) dealloc; + +- (SourceCell *) initWithSource:(Source *)source; + +@end + +@implementation SourceCell + +- (void) dealloc { + [icon_ release]; + [origin_ release]; + [description_ release]; + [label_ release]; + [super dealloc]; +} + +- (SourceCell *) initWithSource:(Source *)source { + if ((self = [super init]) != nil) { + if (icon_ == nil) + icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]]; + if (icon_ == nil) + icon_ = [UIImage applicationImageNamed:@"unknown.png"]; + icon_ = [icon_ retain]; + + origin_ = [[source name] retain]; + label_ = [[source uri] retain]; + description_ = [[source description] retain]; + } return self; +} + +- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected { + float width(rect.size.width); + + if (icon_ != nil) + [icon_ drawInRect:CGRectMake(10, 10, 30, 30)]; + + if (selected) + UISetColor(White_); + + if (!selected) + UISetColor(Black_); + [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - 80) withFont:Font18Bold_ ellipsis:2]; + + if (!selected) + UISetColor(Blue_); + [label_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ ellipsis:2]; + + if (!selected) + UISetColor(Gray_); + [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 40) withFont:Font14_ ellipsis:2]; + + [super drawContentInRect:rect selected:selected]; +} + +@end +/* }}} */ +/* Source Table {{{ */ +@interface SourceTable : RVPage { + _transient Database *database_; + UISectionList *list_; + NSMutableArray *sources_; + UIActionSheet *alert_; + int offset_; + + NSString *href_; + UIProgressHUD *hud_; + NSError *error_; + + //NSURLConnection *installer_; + NSURLConnection *trivial_; + NSURLConnection *trivial_bz2_; + NSURLConnection *trivial_gz_; + //NSURLConnection *automatic_; + + BOOL cydia_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; + +@end + +@implementation SourceTable + +- (void) _deallocConnection:(NSURLConnection *)connection { + if (connection != nil) { + [connection cancel]; + //[connection setDelegate:nil]; + [connection release]; + } +} + +- (void) dealloc { + [[list_ table] setDelegate:nil]; + [list_ setDataSource:nil]; + + if (href_ != nil) + [href_ release]; + if (hud_ != nil) + [hud_ release]; + if (error_ != nil) + [error_ release]; + + //[self _deallocConnection:installer_]; + [self _deallocConnection:trivial_]; + [self _deallocConnection:trivial_gz_]; + [self _deallocConnection:trivial_bz2_]; + //[self _deallocConnection:automatic_]; + + [sources_ release]; + [list_ release]; + [super dealloc]; +} + +- (int) numberOfSectionsInSectionList:(UISectionList *)list { + return offset_ == 0 ? 1 : 2; +} + +- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section { + switch (section + (offset_ == 0 ? 1 : 0)) { + case 0: return UCLocalize("ENTERED_BY_USER"); + case 1: return UCLocalize("INSTALLED_BY_PACKAGE"); + + _nodefault + } +} + +- (int) sectionList:(UISectionList *)list rowForSection:(int)section { + switch (section + (offset_ == 0 ? 1 : 0)) { + case 0: return 0; + case 1: return offset_; + + _nodefault + } +} + +- (int) numberOfRowsInTable:(UITable *)table { + return [sources_ count]; +} + +- (float) table:(UITable *)table heightForRow:(int)row { + Source *source = [sources_ objectAtIndex:row]; + return [source description] == nil ? 56 : 73; +} + +- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col { + Source *source = [sources_ objectAtIndex:row]; + // XXX: weird warning, stupid selectors ;P + return [[[SourceCell alloc] initWithSource:(id)source] autorelease]; +} + +- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row { + return YES; +} + +- (BOOL) table:(UITable *)table canSelectRow:(int)row { + return YES; +} + +- (void) tableRowSelected:(NSNotification*)notification { + UITable *table([list_ table]); + int row([table selectedRow]); + if (row == INT_MAX) + return; + + Source *source = [sources_ objectAtIndex:row]; + + PackageTable *packages = [[[FilteredPackageTable alloc] + initWithBook:book_ + database:database_ + title:[source label] + filter:@selector(isVisibleInSource:) + with:source + ] autorelease]; + + [packages setDelegate:delegate_]; + + [book_ pushPage:packages]; +} + +- (BOOL) table:(UITable *)table canDeleteRow:(int)row { + Source *source = [sources_ objectAtIndex:row]; + return [source record] != nil; +} + +- (void) table:(UITable *)table willSwipeToDeleteRow:(int)row { + [[list_ table] setDeleteConfirmationRow:row]; +} + +- (void) table:(UITable *)table deleteRow:(int)row { + Source *source = [sources_ objectAtIndex:row]; + [Sources_ removeObjectForKey:[source key]]; + [delegate_ syncData]; +} + +- (void) complete { + [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: + @"deb", @"Type", + href_, @"URI", + @"./", @"Distribution", + nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]]; + + [delegate_ syncData]; +} + +- (NSString *) getWarning { + NSString *href(href_); + NSRange colon([href rangeOfString:@"://"]); + if (colon.location != NSNotFound) + href = [href substringFromIndex:(colon.location + 3)]; + href = [href stringByAddingPercentEscapes]; + href = [CydiaURL(@"api/repotag/") stringByAppendingString:href]; + href = [href stringByCachingURLWithCurrentCDN]; + + NSURL *url([NSURL URLWithString:href]); + + NSStringEncoding encoding; + NSError *error(nil); + + if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error]) + return [warning length] == 0 ? nil : warning; + return nil; +} + +- (void) _endConnection:(NSURLConnection *)connection { + NSURLConnection **field = NULL; + if (connection == trivial_) + field = &trivial_; + else if (connection == trivial_bz2_) + field = &trivial_bz2_; + else if (connection == trivial_gz_) + field = &trivial_gz_; + _assert(field != NULL); + [connection release]; + *field = nil; + + if ( + trivial_ == nil && + trivial_bz2_ == nil && + trivial_gz_ == nil + ) { + bool defer(false); + + if (cydia_) { + if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) { + defer = true; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:UCLocalize("SOURCE_WARNING") + buttons:[NSArray arrayWithObjects:UCLocalize("ADD_ANYWAY"), UCLocalize("CANCEL"), nil] + defaultButtonIndex:0 + delegate:self + context:@"warning" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setNumberOfRows:1]; + [sheet setBodyText:warning]; + [sheet popupAlertAnimated:YES]; + } else + [self complete]; + } else if (error_ != nil) { + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:UCLocalize("VERIFICATION_ERROR") + buttons:[NSArray arrayWithObjects:UCLocalize("OK"), nil] + defaultButtonIndex:0 + delegate:self + context:@"urlerror" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setBodyText:[error_ localizedDescription]]; + [sheet popupAlertAnimated:YES]; + } else { + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:UCLocalize("NOT_REPOSITORY") + buttons:[NSArray arrayWithObjects:UCLocalize("OK"), nil] + defaultButtonIndex:0 + delegate:self + context:@"trivial" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setBodyText:UCLocalize("NOT_REPOSITORY_EX")]; + [sheet popupAlertAnimated:YES]; + } + + [delegate_ setStatusBarShowsProgress:NO]; + [delegate_ removeProgressHUD:hud_]; + + [hud_ autorelease]; + hud_ = nil; + + if (!defer) { + [href_ release]; + href_ = nil; + } + + if (error_ != nil) { + [error_ release]; + error_ = nil; + } + } +} + +- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response { + switch ([response statusCode]) { + case 200: + cydia_ = YES; + } +} + +- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]); + if (error_ != nil) + error_ = [error retain]; + [self _endConnection:connection]; +} + +- (void) connectionDidFinishLoading:(NSURLConnection *)connection { + [self _endConnection:connection]; +} + +- (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method { + NSMutableURLRequest *request = [NSMutableURLRequest + requestWithURL:[NSURL URLWithString:href] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:120.0 + ]; + + [request setHTTPMethod:method]; + + if (Machine_ != NULL) + [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; + if (UniqueID_ != nil) + [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; + if (Role_ != nil) + [request setValue:Role_ forHTTPHeaderField:@"X-Role"]; + + return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease]; +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"source"]) { + switch (button) { + case 1: { + NSString *href = [[sheet textField] text]; + + //installer_ = [[self _requestHRef:href method:@"GET"] retain]; + + if (![href hasSuffix:@"/"]) + href_ = [href stringByAppendingString:@"/"]; + else + href_ = href; + href_ = [href_ retain]; + + trivial_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages"] method:@"HEAD"] retain]; + trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain]; + trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain]; + //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain]; + + cydia_ = false; + + hud_ = [[delegate_ addProgressHUD] retain]; + [hud_ setText:UCLocalize("VERIFYING_URL")]; + } break; + + case 2: + break; + + _nodefault + } + + [sheet dismiss]; + } else if ([context isEqualToString:@"trivial"]) + [sheet dismiss]; + else if ([context isEqualToString:@"urlerror"]) + [sheet dismiss]; + else if ([context isEqualToString:@"warning"]) { + switch (button) { + case 1: + [self complete]; + break; + + case 2: + break; + + _nodefault + } + + [href_ release]; + href_ = nil; + + [sheet dismiss]; + } +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + sources_ = [[NSMutableArray arrayWithCapacity:16] retain]; + + //list_ = [[UITable alloc] initWithFrame:[self bounds]]; + list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO]; + [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [self addSubview:list_]; + + [list_ setShouldHideHeaderInShortLists:NO]; + [list_ setDataSource:self]; + + UITableColumn *column = [[UITableColumn alloc] + initWithTitle:UCLocalize("NAME") + identifier:@"name" + width:[self frame].size.width + ]; + + UITable *table = [list_ table]; + [table setSeparatorStyle:1]; + [table addTableColumn:column]; + [table setDelegate:self]; + + [self reloadData]; + + [self setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + } return self; +} + +- (void) reloadData { + pkgSourceList list; + if (!list.ReadMainList()) + return; + + [sources_ removeAllObjects]; + [sources_ addObjectsFromArray:[database_ sources]]; + _trace(); + [sources_ sortUsingSelector:@selector(compareByNameAndType:)]; + _trace(); + + int count([sources_ count]); + for (offset_ = 0; offset_ != count; ++offset_) { + Source *source = [sources_ objectAtIndex:offset_]; + if ([source record] == nil) + break; + } + + [list_ reloadData]; +} + +- (void) resetViewAnimated:(BOOL)animated { + [list_ resetViewAnimated:animated]; +} + +- (void) _leftButtonClicked { + /*[book_ pushPage:[[[AddSourceView alloc] + initWithBook:book_ + database:database_ + ] autorelease]];*/ + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:UCLocalize("ENTER_APT_URL") + buttons:[NSArray arrayWithObjects:UCLocalize("ADD_SOURCE"), UCLocalize("CANCEL"), nil] + defaultButtonIndex:0 + delegate:self + context:@"source" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setNumberOfRows:1]; + [sheet addTextFieldWithValue:@"http://" label:@""]; + + UITextInputTraits *traits = [[sheet textField] textInputTraits]; + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setKeyboardType:UIKeyboardTypeURL]; + // XXX: UIReturnKeyDone + [traits setReturnKeyType:UIReturnKeyNext]; + + [sheet popupAlertAnimated:YES]; +} + +- (void) _rightButtonClicked { + UITable *table = [list_ table]; + BOOL editing = [table isRowDeletionEnabled]; + [table enableRowDeletion:!editing animated:YES]; + [book_ reloadButtonsForPage:self]; +} + +- (NSString *) title { + return UCLocalize("SOURCES"); +} + +- (NSString *) leftButtonTitle { + return [[list_ table] isRowDeletionEnabled] ? UCLocalize("ADD") : nil; +} + +- (id) rightButtonTitle { + return [[list_ table] isRowDeletionEnabled] ? UCLocalize("DONE") : UCLocalize("EDIT"); +} + +- (UINavigationButtonStyle) rightButtonStyle { + return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal; +} + +@end +/* }}} */ + +/* Installed View {{{ */ +@interface InstalledView : RVPage { + _transient Database *database_; + FilteredPackageTable *packages_; + BOOL expert_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; + +@end + +@implementation InstalledView + +- (void) dealloc { + [packages_ release]; + [super dealloc]; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + + packages_ = [[FilteredPackageTable alloc] + initWithBook:book + database:database + title:nil + filter:@selector(isInstalledAndVisible:) + with:[NSNumber numberWithBool:YES] + ]; + + [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [self addSubview:packages_]; + + [self setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + } return self; +} + +- (void) resetViewAnimated:(BOOL)animated { + [packages_ resetViewAnimated:animated]; +} + +- (void) reloadData { + [packages_ reloadData]; +} + +- (void) _rightButtonClicked { + [packages_ setObject:[NSNumber numberWithBool:expert_]]; + [packages_ reloadData]; + expert_ = !expert_; + [book_ reloadButtonsForPage:self]; +} + +- (NSString *) title { + return UCLocalize("INSTALLED"); +} + +- (NSString *) backButtonTitle { + return UCLocalize("PACKAGES"); +} + +- (id) rightButtonTitle { + return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE"); +} + +- (UINavigationButtonStyle) rightButtonStyle { + return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal; +} + +- (void) setDelegate:(id)delegate { + [super setDelegate:delegate]; + [packages_ setDelegate:delegate]; +} + +@end +/* }}} */ + +/* Home View {{{ */ +@interface HomeView : CydiaBrowserView { +} + +@end + +@implementation HomeView + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"about"]) + [sheet dismiss]; + else + [super alertSheet:sheet buttonClicked:button]; +} + +- (void) _setMoreHeaders:(NSMutableURLRequest *)request { + [super _setMoreHeaders:request]; + if (ChipID_ != nil) + [request setValue:ChipID_ forHTTPHeaderField:@"X-Chip-ID"]; + if (UniqueID_ != nil) + [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; +} + +- (void) _leftButtonClicked { + UIAlertView *alert = [[[UIAlertView alloc] init] autorelease]; + [alert setTitle:UCLocalize("ABOUT_CYDIA")]; + [alert addButtonWithTitle:UCLocalize("CLOSE")]; + [alert setCancelButtonIndex:0]; + + [alert setMessage: + @"Copyright (C) 2008-2010\n" + "Jay Freeman (saurik)\n" + "saurik@saurik.com\n" + "http://www.saurik.com/" + ]; + + [alert show]; +} + +- (NSString *) leftButtonTitle { + return UCLocalize("ABOUT"); +} + +@end +/* }}} */ +/* Manage View {{{ */ +@interface ManageView : CydiaBrowserView { +} + +@end + +@implementation ManageView + +- (NSString *) title { + return UCLocalize("MANAGE"); +} + +- (void) _leftButtonClicked { + [delegate_ askForSettings]; + [delegate_ updateData]; +} + +- (NSString *) leftButtonTitle { + return UCLocalize("SETTINGS"); +} + +#if !AlwaysReload +- (id) _rightButtonTitle { + return Queuing_ ? UCLocalize("QUEUE") : nil; +} + +- (UINavigationButtonStyle) rightButtonStyle { + return Queuing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal; +} + +- (void) _rightButtonClicked { + [delegate_ queue]; +} +#endif + +- (bool) isLoading { + return false; +} + +@end +/* }}} */ + +/* Cydia Book {{{ */ +@interface CYBook : RVBook < + ProgressDelegate +> { + _transient Database *database_; + UINavigationBar *overlay_; + UINavigationBar *underlay_; + UIProgressIndicator *indicator_; + UITextLabel *prompt_; + UIProgressBar *progress_; + UINavigationButton *cancel_; + bool updating_; + bool dropped_; +} + +- (id) initWithFrame:(CGRect)frame database:(Database *)database; +- (void) update; +- (BOOL) updating; +- (void) setUpdate:(NSDate *)date; + +@end + +@implementation CYBook + +- (void) dealloc { + [overlay_ release]; + [indicator_ release]; + [prompt_ release]; + [progress_ release]; + [cancel_ release]; + [super dealloc]; +} + +- (NSString *) getTitleForPage:(RVPage *)page { + return [super getTitleForPage:page]; +} + +- (BOOL) updating { + return updating_; +} + +- (void) dropBar { + if (dropped_) + return; + dropped_ = true; + + [UIView beginAnimations:nil context:NULL]; + + CGRect ovrframe = [overlay_ frame]; + ovrframe.origin.y = 0; + [overlay_ setFrame:ovrframe]; + + CGRect barframe = [navbar_ frame]; + barframe.origin.y += ovrframe.size.height; + [navbar_ setFrame:barframe]; + + CGRect trnframe = [transition_ frame]; + trnframe.origin.y += ovrframe.size.height; + trnframe.size.height -= ovrframe.size.height; + [transition_ setFrame:trnframe]; + + [UIView endAnimations]; +} + +- (void) raiseBar { + if (!dropped_) + return; + dropped_ = false; + + [UIView beginAnimations:nil context:NULL]; + + CGRect ovrframe = [overlay_ frame]; + ovrframe.origin.y = -ovrframe.size.height; + [overlay_ setFrame:ovrframe]; + + CGRect barframe = [navbar_ frame]; + barframe.origin.y -= ovrframe.size.height; + [navbar_ setFrame:barframe]; + + CGRect trnframe = [transition_ frame]; + trnframe.origin.y -= ovrframe.size.height; + trnframe.size.height += ovrframe.size.height; + [transition_ setFrame:trnframe]; + + [UIView commitAnimations]; +} + +- (void) setUpdate:(NSDate *)date { + [self update]; +} + +- (void) update { + [self dropBar]; + + [indicator_ startAnimation]; + [prompt_ setText:UCLocalize("UPDATING_DATABASE")]; + [progress_ setProgress:0]; + + updating_ = true; + [overlay_ addSubview:cancel_]; + + [NSThread + detachNewThreadSelector:@selector(_update) + toTarget:self + withObject:nil + ]; +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"refresh"]) + [sheet dismiss]; +} + +- (void) _update_ { + updating_ = false; + + [indicator_ stopAnimation]; + + [self raiseBar]; + + [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0]; +} + +- (id) initWithFrame:(CGRect)frame database:(Database *)database { + if ((self = [super initWithFrame:frame]) != nil) { + database_ = database; + + CGRect ovrrect([navbar_ bounds]); + ovrrect.size.height = [UINavigationBar defaultSize].height; + ovrrect.origin.y = -ovrrect.size.height; + + overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; + [overlay_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [self addSubview:overlay_]; + + ovrrect.origin.y = frame.size.height; + underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; + [underlay_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [self addSubview:underlay_]; + + [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]]; + + [overlay_ setBarStyle:1]; + [underlay_ setBarStyle:1]; + + int barstyle([overlay_ _barStyle:NO]); + bool ugly(barstyle == 0); + + UIProgressIndicatorStyle style = ugly ? + UIProgressIndicatorStyleMediumBrown : + UIProgressIndicatorStyleMediumWhite; + + CGSize indsize([UIProgressIndicator defaultSizeForStyle:style]); + unsigned indoffset = (ovrrect.size.height - indsize.height) / 2; + CGRect indrect = {{indoffset, indoffset}, indsize}; + + indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect]; + [indicator_ setStyle:style]; + [overlay_ addSubview:indicator_]; + + CGSize prmsize = {215, indsize.height + 4}; + + CGRect prmrect = {{ + indoffset * 2 + indsize.width, + unsigned(ovrrect.size.height - prmsize.height) / 2 - 1 + }, prmsize}; + + UIFont *font([UIFont systemFontOfSize:15]); + + prompt_ = [[UITextLabel alloc] initWithFrame:prmrect]; + + [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]]; + [prompt_ setBackgroundColor:[UIColor clearColor]]; + [prompt_ setFont:font]; + + [overlay_ addSubview:prompt_]; + + CGSize prgsize = {75, 100}; + + CGRect prgrect = {{ + ovrrect.size.width - prgsize.width - 10, + (ovrrect.size.height - prgsize.height) / 2 + } , prgsize}; + + progress_ = [[UIProgressBar alloc] initWithFrame:prgrect]; + [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [overlay_ addSubview:progress_]; + + [progress_ setStyle:0]; + + cancel_ = [[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted]; + [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin]; + [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside]; + + CGRect frame = [cancel_ frame]; + frame.origin.x = ovrrect.size.width - frame.size.width - 5; + frame.origin.y = (ovrrect.size.height - frame.size.height) / 2; + [cancel_ setFrame:frame]; + + [cancel_ setBarStyle:barstyle]; + } return self; +} + +- (void) _onCancel { + updating_ = false; + [cancel_ removeFromSuperview]; +} + +- (void) _update { _pooled + Status status; + status.setDelegate(self); + [database_ updateWithStatus:status]; + + [self + performSelectorOnMainThread:@selector(_update_) + withObject:nil + waitUntilDone:NO + ]; +} + +- (void) setProgressError:(NSString *)error withTitle:(NSString *)title { + [prompt_ setText:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]]; +} + +/* + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), UCLocalize("REFRESH")] + buttons:[NSArray arrayWithObjects: + UCLocalize("OK"), + nil] + defaultButtonIndex:0 + delegate:self + context:@"refresh" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setBodyText:error]; + [sheet popupAlertAnimated:YES]; + + [self reloadButtons]; +*/ + +- (void) setProgressTitle:(NSString *)title { + [self + performSelectorOnMainThread:@selector(_setProgressTitle:) + withObject:title + waitUntilDone:YES + ]; +} + +- (void) setProgressPercent:(float)percent { + [self + performSelectorOnMainThread:@selector(_setProgressPercent:) + withObject:[NSNumber numberWithFloat:percent] + waitUntilDone:YES + ]; +} + +- (void) startProgress { +} + +- (void) addProgressOutput:(NSString *)output { + [self + performSelectorOnMainThread:@selector(_addProgressOutput:) + withObject:output + waitUntilDone:YES + ]; +} + +- (bool) isCancelling:(size_t)received { + return !updating_; +} + +- (void) _setProgressTitle:(NSString *)title { + [prompt_ setText:title]; +} + +- (void) _setProgressPercent:(NSNumber *)percent { + [progress_ setProgress:[percent floatValue]]; +} + +- (void) _addProgressOutput:(NSString *)output { +} + +@end +/* }}} */ +/* Cydia:// Protocol {{{ */ +@interface CydiaURLProtocol : NSURLProtocol { +} + +@end + +@implementation CydiaURLProtocol + ++ (BOOL) canInitWithRequest:(NSURLRequest *)request { + NSURL *url([request URL]); + if (url == nil) + return NO; + NSString *scheme([[url scheme] lowercaseString]); + if (scheme == nil || ![scheme isEqualToString:@"cydia"]) + return NO; + return YES; +} + ++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { + return request; +} + +- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request { + id client([self client]); + if (icon == nil) + [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]]; + else { + NSData *data(UIImagePNGRepresentation(icon)); + + NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]); + [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; + [client URLProtocol:self didLoadData:data]; + [client URLProtocolDidFinishLoading:self]; + } +} + +- (void) startLoading { + id client([self client]); + NSURLRequest *request([self request]); + + NSURL *url([request URL]); + NSString *href([url absoluteString]); + + NSString *path([href substringFromIndex:8]); + NSRange slash([path rangeOfString:@"/"]); + + NSString *command; + if (slash.location == NSNotFound) { + command = path; + path = nil; + } else { + command = [path substringToIndex:slash.location]; + path = [path substringFromIndex:(slash.location + 1)]; + } + + Database *database([Database sharedInstance]); + + if ([command isEqualToString:@"package-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + Package *package([database packageWithName:path]); + if (package == nil) + goto fail; + UIImage *icon([package icon]); + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"source-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *source(Simplify(path)); + UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]); + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"uikit-image"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + UIImage *icon(_UIImageWithName(path)); + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"section-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *section(Simplify(path)); + UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]); + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + [self _returnPNGWithImage:icon forRequest:request]; + } else fail: { + [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]]; + } +} + +- (void) stopLoading { +} + +@end +/* }}} */ + +/* Sections View {{{ */ +@interface SectionsView : RVPage { + _transient Database *database_; + NSMutableArray *sections_; + NSMutableArray *filtered_; + UITable *list_; + UIView *accessory_; + BOOL editing_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; +- (void) reloadData; +- (void) resetView; + +@end + +@implementation SectionsView + +- (void) dealloc { + [list_ setDataSource:nil]; + [list_ setDelegate:nil]; + + [sections_ release]; + [filtered_ release]; + [list_ release]; + [accessory_ release]; + [super dealloc]; +} + +- (int) numberOfRowsInTable:(UITable *)table { + return editing_ ? [sections_ count] : [filtered_ count] + 1; +} + +- (float) table:(UITable *)table heightForRow:(int)row { + return 45; +} + +- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing { + if (reusing == nil) + reusing = [[[SectionCell alloc] init] autorelease]; + [(SectionCell *)reusing setSection:(editing_ ? + [sections_ objectAtIndex:row] : + (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)]) + ) editing:editing_]; + return reusing; +} + +- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row { + return !editing_; +} + +- (BOOL) table:(UITable *)table canSelectRow:(int)row { + return !editing_; +} + +- (void) tableRowSelected:(NSNotification *)notification { + int row = [[notification object] selectedRow]; + if (row == INT_MAX) + return; + + Section *section; + NSString *name; + NSString *title; + + if (row == 0) { + section = nil; + name = nil; + title = UCLocalize("ALL_PACKAGES"); + } else { + section = [filtered_ objectAtIndex:(row - 1)]; + name = [section name]; + + if (name != nil) { + name = [NSString stringWithString:name]; + title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"]; + } else { + name = @""; + title = UCLocalize("NO_SECTION"); + } + } + + PackageTable *table = [[[FilteredPackageTable alloc] + initWithBook:book_ + database:database_ + title:title + filter:@selector(isVisibleInSection:) + with:name + ] autorelease]; + + [table setDelegate:delegate_]; + + [book_ pushPage:table]; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + + sections_ = [[NSMutableArray arrayWithCapacity:16] retain]; + filtered_ = [[NSMutableArray arrayWithCapacity:16] retain]; + + list_ = [[UITable alloc] initWithFrame:[self bounds]]; + [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [self addSubview:list_]; + + UITableColumn *column = [[[UITableColumn alloc] + initWithTitle:UCLocalize("NAME") + identifier:@"name" + width:[self frame].size.width + ] autorelease]; + + [list_ setDataSource:self]; + [list_ setSeparatorStyle:1]; + [list_ addTableColumn:column]; + [list_ setDelegate:self]; + [list_ setReusesTableCells:YES]; + + [self reloadData]; + + [self setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + } return self; +} + +- (void) reloadData { + NSArray *packages = [database_ packages]; + + [sections_ removeAllObjects]; + [filtered_ removeAllObjects]; + +#if 0 + typedef __gnu_cxx::hash_map SectionMap; + SectionMap sections; + sections.resize(64); +#else + NSMutableDictionary *sections([NSMutableDictionary dictionaryWithCapacity:32]); +#endif + + _trace(); + for (Package *package in packages) { + NSString *name([package section]); + NSString *key(name == nil ? @"" : name); + +#if 0 + Section **section; + + _profile(SectionsView$reloadData$Section) + section = §ions[key]; + if (*section == nil) { + _profile(SectionsView$reloadData$Section$Allocate) + *section = [[[Section alloc] initWithName:name localize:YES] autorelease]; + _end + } + _end + + [*section addToCount]; + + _profile(SectionsView$reloadData$Filter) + if (![package valid] || ![package visible]) + continue; + _end + + [*section addToRow]; +#else + Section *section; + + _profile(SectionsView$reloadData$Section) + section = [sections objectForKey:key]; + if (section == nil) { + _profile(SectionsView$reloadData$Section$Allocate) + section = [[[Section alloc] initWithName:name localize:YES] autorelease]; + [sections setObject:section forKey:key]; + _end + } + _end + + [section addToCount]; + + _profile(SectionsView$reloadData$Filter) + if (![package valid] || ![package visible]) + continue; + _end + + [section addToRow]; +#endif + } + _trace(); + +#if 0 + for (SectionMap::const_iterator i(sections.begin()), e(sections.end()); i != e; ++i) + [sections_ addObject:i->second]; +#else + [sections_ addObjectsFromArray:[sections allValues]]; +#endif + + [sections_ sortUsingSelector:@selector(compareByLocalized:)]; + + for (Section *section in sections_) { + size_t count([section row]); + if (count == 0) + continue; + + section = [[[Section alloc] initWithName:[section name] localized:[section localized]] autorelease]; + [section setCount:count]; + [filtered_ addObject:section]; + } + + [list_ reloadData]; + _trace(); +} + +- (void) resetView { + if (editing_) + [self _rightButtonClicked]; +} + +- (void) resetViewAnimated:(BOOL)animated { + [list_ resetViewAnimated:animated]; +} + +- (void) _rightButtonClicked { + if ((editing_ = !editing_)) + [list_ reloadData]; + else + [delegate_ updateData]; + [book_ reloadTitleForPage:self]; + [book_ reloadButtonsForPage:self]; +} + +- (NSString *) title { + return editing_ ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS"); +} + +- (NSString *) backButtonTitle { + return UCLocalize("SECTIONS"); +} + +- (id) rightButtonTitle { + return [sections_ count] == 0 ? nil : editing_ ? UCLocalize("DONE") : UCLocalize("EDIT"); +} + +- (UINavigationButtonStyle) rightButtonStyle { + return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal; +} + +- (UIView *) accessoryView { + return accessory_; +} + +@end +/* }}} */ +/* Changes View {{{ */ +@interface ChangesView : RVPage { + _transient Database *database_; + NSMutableArray *packages_; + NSMutableArray *sections_; + UITableView *list_; + unsigned upgrades_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database delegate:(id)delegate; +- (void) reloadData; + +@end + +@implementation ChangesView + +- (void) dealloc { + [list_ setDelegate:nil]; + [list_ setDataSource:nil]; + + [packages_ release]; + [sections_ release]; + [list_ release]; + [super dealloc]; +} + +- (NSInteger) numberOfSectionsInTableView:(UITableView *)list { + NSInteger count([sections_ count]); + return count == 0 ? 1 : count; +} + +- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section { + if ([sections_ count] == 0) + return nil; + return [[sections_ objectAtIndex:section] name]; +} + +- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section { + if ([sections_ count] == 0) + return 0; + return [[sections_ objectAtIndex:section] count]; +} + +- (Package *) packageAtIndexPath:(NSIndexPath *)path { + Section *section([sections_ objectAtIndex:[path section]]); + NSInteger row([path row]); + return [packages_ objectAtIndex:([section row] + row)]; +} + +- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path { + PackageCell *cell([table dequeueReusableCellWithIdentifier:@"Package"]); + if (cell == nil) + cell = [[[PackageCell alloc] init] autorelease]; + [cell setPackage:[self packageAtIndexPath:path]]; + return cell; +} + +- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path { + return 73; + return [PackageCell heightForPackage:[self packageAtIndexPath:path]]; +} + +- (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path { + Package *package([self packageAtIndexPath:path]); + PackageView *view([delegate_ packageView]); + [view setDelegate:delegate_]; + [view setPackage:package]; + [book_ pushPage:view]; + return path; +} + +- (void) _leftButtonClicked { + [(CYBook *)book_ update]; + [self reloadButtons]; +} + +- (void) _rightButtonClicked { + [delegate_ distUpgrade]; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database delegate:(id)delegate { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + + packages_ = [[NSMutableArray arrayWithCapacity:16] retain]; + sections_ = [[NSMutableArray arrayWithCapacity:16] retain]; + + list_ = [[UITableView alloc] initWithFrame:[self bounds] style:UITableViewStylePlain]; + [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [self addSubview:list_]; + + //XXX:[list_ setShouldHideHeaderInShortLists:NO]; + [list_ setDataSource:self]; + [list_ setDelegate:self]; + //[list_ setSectionListStyle:1]; + + delegate_ = delegate; + [self reloadData]; + + [self setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + } return self; +} + +- (void) _reloadPackages:(NSArray *)packages { + _trace(); + for (Package *package in packages) + if ( + [package uninstalled] && [package valid] && [package visible] || + [package upgradableAndEssential:YES] + ) + [packages_ addObject:package]; + + _trace(); + [packages_ radixSortUsingFunction:reinterpret_cast(&PackageChangesRadix) withContext:NULL]; + _trace(); +} + +- (void) reloadData { + NSArray *packages = [database_ packages]; + + [packages_ removeAllObjects]; + [sections_ removeAllObjects]; + + UIProgressHUD *hud([delegate_ addProgressHUD]); + // XXX: localize + [hud setText:@"Loading Changes"]; + NSLog(@"HUD:%@::%@", delegate_, hud); + [self yieldToSelector:@selector(_reloadPackages:) withObject:packages]; + [delegate_ removeProgressHUD:hud]; + + Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease]; + Section *ignored = [[[Section alloc] initWithName:UCLocalize("IGNORED_UPGRADES") localize:NO] autorelease]; + Section *section = nil; + NSDate *last = nil; + + upgrades_ = 0; + bool unseens = false; + + CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle)); + + for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) { + Package *package = [packages_ objectAtIndex:offset]; + + BOOL uae = [package upgradableAndEssential:YES]; + + if (!uae) { + unseens = true; + NSDate *seen; + + _profile(ChangesView$reloadData$Remember) + seen = [package seen]; + _end + + if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) { + last = seen; + + NSString *name; + if (seen == nil) + name = UCLocalize("UNKNOWN"); + else { + name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen); + [name autorelease]; + } + + _profile(ChangesView$reloadData$Allocate) + name = [NSString stringWithFormat:UCLocalize("NEW_AT"), name]; + section = [[[Section alloc] initWithName:name row:offset localize:NO] autorelease]; + [sections_ addObject:section]; + _end + } + + [section addToCount]; + } else if ([package ignored]) + [ignored addToCount]; + else { + ++upgrades_; + [upgradable addToCount]; + } + } + _trace(); + + CFRelease(formatter); + + if (unseens) { + Section *last = [sections_ lastObject]; + size_t count = [last count]; + [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)]; + [sections_ removeLastObject]; + } + + if ([ignored count] != 0) + [sections_ insertObject:ignored atIndex:0]; + if (upgrades_ != 0) + [sections_ insertObject:upgradable atIndex:0]; + + [list_ reloadData]; + [self reloadButtons]; +} + +- (void) resetViewAnimated:(BOOL)animated { + [list_ resetViewAnimated:animated]; +} + +- (NSString *) leftButtonTitle { + return [(CYBook *)book_ updating] ? nil : UCLocalize("REFRESH"); +} + +- (id) rightButtonTitle { + return upgrades_ == 0 ? nil : [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]]; +} + +- (NSString *) title { + return UCLocalize("CHANGES"); +} + +@end +/* }}} */ +/* Search View {{{ */ +@protocol SearchViewDelegate +- (void) showKeyboard:(BOOL)show; +@end + +@interface SearchView : RVPage { + UIView *accessory_; + UISearchField *field_; + FilteredPackageTable *table_; + bool reload_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database; +- (void) reloadData; + +@end + +@implementation SearchView + +- (void) dealloc { + [field_ setDelegate:nil]; + + [accessory_ release]; + [field_ release]; + [table_ release]; + [super dealloc]; +} + +- (void) _showKeyboard:(BOOL)show { + CGSize keysize = [UIKeyboard defaultSize]; + CGRect keydown = [book_ pageBounds]; + CGRect keyup = keydown; + keyup.size.height -= keysize.height - ButtonBarHeight_; + + float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height; + + UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease]; + [animation setSignificantRectFields:8]; + + if (show) { + [animation setStartFrame:keydown]; + [animation setEndFrame:keyup]; + } else { + [animation setStartFrame:keyup]; + [animation setEndFrame:keydown]; + } + + UIAnimator *animator = [UIAnimator sharedAnimator]; + + [animator + addAnimations:[NSArray arrayWithObjects:animation, nil] + withDuration:(KeyboardTime_ - delay) + start:!show + ]; + + if (show) + [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay]; + + //[delegate_ showKeyboard:show]; +} + +- (void) textFieldDidBecomeFirstResponder:(UITextField *)field { + [self _showKeyboard:YES]; + [table_ setObject:[field_ text] forFilter:@selector(isUnfilteredAndSelectedForBy:)]; + [self reloadData]; +} + +- (void) textFieldDidResignFirstResponder:(UITextField *)field { + [self _showKeyboard:NO]; + [table_ setObject:[field_ text] forFilter:@selector(isUnfilteredAndSearchedForBy:)]; + [self reloadData]; +} + +- (void) keyboardInputChanged:(UIFieldEditor *)editor { + if (reload_) { + NSString *text([field_ text]); + [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)]; + [table_ setObject:text forFilter:@selector(isUnfilteredAndSelectedForBy:)]; + [self reloadData]; + reload_ = false; + } +} + +- (void) textFieldClearButtonPressed:(UITextField *)field { + reload_ = true; +} + +- (void) keyboardInputShouldDelete:(id)input { + reload_ = true; +} + +- (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked { + if ([text length] != 1 || [text characterAtIndex:0] != '\n') { + reload_ = true; + return YES; + } else { + [field_ resignFirstResponder]; + return NO; + } +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database { + if ((self = [super initWithBook:book]) != nil) { + CGRect pageBounds = [book_ pageBounds]; + + table_ = [[FilteredPackageTable alloc] + initWithBook:book + database:database + title:nil + filter:@selector(isUnfilteredAndSearchedForBy:) + with:nil + ]; + + [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [self addSubview:table_]; + + [table_ setShouldHideHeaderInShortLists:NO]; + + CGRect cnfrect = {{7, 38}, {17, 18}}; + + CGRect area; + + area.origin.x = 10; + area.origin.y = 1; + + area.size.width = [self bounds].size.width - area.origin.x * 2; + area.size.height = [UISearchField defaultHeight]; + + field_ = [[UISearchField alloc] initWithFrame:area]; + [field_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + + UIFont *font = [UIFont systemFontOfSize:16]; + [field_ setFont:font]; + + [field_ setPlaceholder:UCLocalize("SEARCH_EX")]; + [field_ setDelegate:self]; + + [field_ setPaddingTop:5]; + + UITextInputTraits *traits([field_ textInputTraits]); + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setReturnKeyType:UIReturnKeySearch]; + + CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}}; + + accessory_ = [[UIView alloc] initWithFrame:accrect]; + [accessory_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [accessory_ addSubview:field_]; + + [self setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + } return self; +} + +- (void) resetViewAnimated:(BOOL)animated { + [table_ resetViewAnimated:animated]; +} + +- (void) _reloadData { +} + +- (void) reloadData { + _profile(SearchView$reloadData) + [table_ reloadData]; + _end + PrintTimes(); + [table_ resetCursor]; +} + +- (UIView *) accessoryView { + return accessory_; +} + +- (NSString *) title { + return nil; +} + +- (NSString *) backButtonTitle { + return UCLocalize("SEARCH"); +} + +- (void) setDelegate:(id)delegate { + [table_ setDelegate:delegate]; + [super setDelegate:delegate]; +} + +@end +/* }}} */ +/* Settings View {{{ */ +@interface SettingsView : RVPage { + _transient Database *database_; + NSString *name_; + Package *package_; + UIPreferencesTable *table_; + _UISwitchSlider *subscribedSwitch_; + _UISwitchSlider *ignoredSwitch_; + UIPreferencesControlTableCell *subscribedCell_; + UIPreferencesControlTableCell *ignoredCell_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package; + +@end + +@implementation SettingsView + +- (void) dealloc { + [table_ setDataSource:nil]; + + [name_ release]; + if (package_ != nil) + [package_ release]; + [table_ release]; + [subscribedSwitch_ release]; + [ignoredSwitch_ release]; + [subscribedCell_ release]; + [ignoredCell_ release]; + [super dealloc]; +} + +- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table { + if (package_ == nil) + return 0; + + return 2; +} + +- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group { + if (package_ == nil) + return nil; + + switch (group) { + case 0: return nil; + case 1: return nil; + + _nodefault + } + + return nil; +} + +- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group { + if (package_ == nil) + return NO; + + switch (group) { + case 0: return NO; + case 1: return YES; + + _nodefault + } + + return NO; +} + +- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group { + if (package_ == nil) + return 0; + + switch (group) { + case 0: return 1; + case 1: return 1; + + _nodefault + } + + return 0; +} + +- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key { + if (package_ == nil) + return; + + _UISwitchSlider *slider([cell control]); + BOOL value([slider value] != 0); + NSMutableDictionary *metadata([package_ metadata]); + + BOOL before; + if (NSNumber *number = [metadata objectForKey:key]) + before = [number boolValue]; + else + before = NO; + + if (value != before) { + [metadata setObject:[NSNumber numberWithBool:value] forKey:key]; + Changed_ = true; + [delegate_ updateData]; + } +} + +- (void) onSubscribed:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsSubscribed"]; +} + +- (void) onIgnored:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsIgnored"]; +} + +- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group { + if (package_ == nil) + return nil; + + switch (group) { + case 0: switch (row) { + case 0: + return subscribedCell_; + case 1: + return ignoredCell_; + _nodefault + } break; + + case 1: switch (row) { + case 0: { + UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]); + [cell setShowSelection:NO]; + [cell setTitle:UCLocalize("SHOW_ALL_CHANGES_EX")]; + return cell; + } + + _nodefault + } break; + + _nodefault + } + + return nil; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package { + if ((self = [super initWithBook:book])) { + database_ = database; + name_ = [package retain]; + + table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]]; + [self addSubview:table_]; + + subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:UIControlEventTouchUpInside]; + + ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventTouchUpInside]; + + subscribedCell_ = [[UIPreferencesControlTableCell alloc] init]; + [subscribedCell_ setShowSelection:NO]; + [subscribedCell_ setTitle:UCLocalize("SHOW_ALL_CHANGES")]; + [subscribedCell_ setControl:subscribedSwitch_]; + + ignoredCell_ = [[UIPreferencesControlTableCell alloc] init]; + [ignoredCell_ setShowSelection:NO]; + [ignoredCell_ setTitle:UCLocalize("IGNORE_UPGRADES")]; + [ignoredCell_ setControl:ignoredSwitch_]; + + [table_ setDataSource:self]; + [self reloadData]; + } return self; +} + +- (void) resetViewAnimated:(BOOL)animated { + [table_ resetViewAnimated:animated]; +} + +- (void) reloadData { + if (package_ != nil) + [package_ autorelease]; + package_ = [database_ packageWithName:name_]; + if (package_ != nil) { + [package_ retain]; + [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO]; + [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO]; + } + + [table_ reloadData]; +} + +- (NSString *) title { + return UCLocalize("SETTINGS"); +} + +@end +/* }}} */ + +/* Signature View {{{ */ +@interface SignatureView : CydiaBrowserView { + _transient Database *database_; + NSString *package_; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package; + +@end + +@implementation SignatureView + +- (void) dealloc { + [package_ release]; + [super dealloc]; +} + +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + // XXX: dude! + [super webView:sender didClearWindowObject:window forFrame:frame]; +} + +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package { + if ((self = [super initWithBook:book]) != nil) { + database_ = database; + package_ = [package retain]; + [self reloadData]; + } return self; +} + +- (void) resetViewAnimated:(BOOL)animated { +} + +- (void) reloadData { + [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]]; +} + +@end +/* }}} */ + +@interface CydiaViewController : UIViewController { +} + +@end + +@implementation CydiaViewController + +- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation { + return NO; // XXX: return YES; +} + +@end + +@interface Cydia : UIApplication < + ConfirmationViewDelegate, + ProgressViewDelegate, + SearchViewDelegate, + CydiaDelegate +> { + UIWindow *window_; + CydiaViewController *root_; + + UIView *underlay_; + UIView *overlay_; + CYBook *book_; + + NSArray *items_; + UITabBar *toolbar_; + + RVBook *confirm_; + + NSMutableArray *essential_; + NSMutableArray *broken_; + + Database *database_; + ProgressView *progress_; + + int tag_; + + UIKeyboard *keyboard_; + UIProgressHUD *hud_; + + SectionsView *sections_; + ChangesView *changes_; + ManageView *manage_; + SearchView *search_; + +#if RecyclePackageViews + NSMutableArray *details_; +#endif +} + +- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class; +- (void) setPage:(RVPage *)page; + +@end + +static _finline void _setHomePage(Cydia *self) { + [self setPage:[self _pageForURL:[NSURL URLWithString:CydiaURL(@"")] withClass:[HomeView class]]]; +} + +@implementation Cydia + +- (UIView *) rotatingContentViewForWindow:(UIWindow *)window { + return window_; +} + +- (void) _loaded { + if ([broken_ count] != 0) { + int count = [broken_ count]; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:(count == 1 ? UCLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:UCLocalize("HALFINSTALLED_PACKAGES"), count]) + buttons:[NSArray arrayWithObjects: + UCLocalize("FORCIBLY_CLEAR"), + UCLocalize("TEMPORARY_IGNORE"), + nil] + defaultButtonIndex:0 + delegate:self + context:@"fixhalf" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setBodyText:UCLocalize("HALFINSTALLED_PACKAGE_EX")]; + [sheet popupAlertAnimated:YES]; + } else if (!Ignored_ && [essential_ count] != 0) { + int count = [essential_ count]; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:(count == 1 ? UCLocalize("ESSENTIAL_UPGRADE") : [NSString stringWithFormat:UCLocalize("ESSENTIAL_UPGRADES"), count]) + buttons:[NSArray arrayWithObjects: + UCLocalize("UPGRADE_ESSENTIAL"), + UCLocalize("COMPLETE_UPGRADE"), + UCLocalize("TEMPORARY_IGNORE"), + nil] + defaultButtonIndex:0 + delegate:self + context:@"upgrade" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setBodyText:UCLocalize("ESSENTIAL_UPGRADE_EX")]; + [sheet popupAlertAnimated:YES]; + } +} + +- (void) _saveConfig { + if (Changed_) { + _trace(); + NSString *error(nil); + if (NSData *data = [NSPropertyListSerialization dataFromPropertyList:Metadata_ format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error]) { + _trace(); + NSError *error(nil); + if (![data writeToFile:@"/var/lib/cydia/metadata.plist" options:NSAtomicWrite error:&error]) + NSLog(@"failure to save metadata data: %@", error); + _trace(); + } else { + NSLog(@"failure to serialize metadata: %@", error); + return; + } + + Changed_ = false; + } +} + +- (void) _updateData { + [self _saveConfig]; + + /* XXX: this is just stupid */ + if (tag_ != 1 && sections_ != nil) + [sections_ reloadData]; + if (tag_ != 2 && changes_ != nil) + [changes_ reloadData]; + if (tag_ != 4 && search_ != nil) + [search_ reloadData]; + + [book_ reloadData]; +} + +- (void) _reloadData { + UIView *block(); + + static bool loaded(false); + UIProgressHUD *hud([self addProgressHUD]); + [hud setText:(loaded ? UCLocalize("RELOADING_DATA") : UCLocalize("LOADING_DATA"))]; + + [database_ yieldToSelector:@selector(reloadData) withObject:nil]; + _trace(); + + [self removeProgressHUD:hud]; + + size_t changes(0); + + [essential_ removeAllObjects]; + [broken_ removeAllObjects]; + + NSArray *packages([database_ packages]); + for (Package *package in packages) { + if ([package half]) + [broken_ addObject:package]; + if ([package upgradableAndEssential:NO]) { + if ([package essential]) + [essential_ addObject:package]; + ++changes; + } + } + + if (changes != 0) { + NSString *badge([[NSNumber numberWithInt:changes] stringValue]); + [toolbar_ setBadgeValue:badge forButton:3]; + if ([toolbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)]) + [toolbar_ setBadgeAnimated:([essential_ count] != 0) forButton:3]; + if ([self respondsToSelector:@selector(setApplicationBadge:)]) + [self setApplicationBadge:badge]; + else + [self setApplicationBadgeString:badge]; + } else { + [toolbar_ setBadgeValue:nil forButton:3]; + if ([toolbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)]) + [toolbar_ setBadgeAnimated:NO forButton:3]; + if ([self respondsToSelector:@selector(removeApplicationBadge)]) + [self removeApplicationBadge]; + else // XXX: maybe use setApplicationBadgeString also? + [self setApplicationIconBadgeNumber:0]; + } + + Queuing_ = false; + [toolbar_ setBadgeValue:nil forButton:4]; + + [self _updateData]; + + if (loaded || ManualRefresh) loaded: + [self _loaded]; + else { + loaded = true; + + NSDate *update([Metadata_ objectForKey:@"LastUpdate"]); + + if (update != nil) { + NSTimeInterval interval([update timeIntervalSinceNow]); + if (interval <= 0 && interval > -(15*60)) + goto loaded; + } + + [book_ setUpdate:update]; + } +} + +- (void) updateData { + [database_ setVisible]; + [self _updateData]; +} + +- (void) update_ { + [database_ update]; +} + +- (void) syncData { + FILE *file(fopen("/etc/apt/sources.list.d/cydia.list", "w")); + _assert(file != NULL); + + for (NSString *key in [Sources_ allKeys]) { + NSDictionary *source([Sources_ objectForKey:key]); + + fprintf(file, "%s %s %s\n", + [[source objectForKey:@"Type"] UTF8String], + [[source objectForKey:@"URI"] UTF8String], + [[source objectForKey:@"Distribution"] UTF8String] + ); + } + + fclose(file); + + [self _saveConfig]; + + [progress_ + detachNewThreadSelector:@selector(update_) + toTarget:self + withObject:nil + title:UCLocalize("UPDATING_SOURCES") + ]; +} + +- (void) reloadData { + @synchronized (self) { + if (confirm_ == nil) + [self _reloadData]; + } +} + +- (void) resolve { + pkgProblemResolver *resolver = [database_ resolver]; + + resolver->InstallProtect(); + if (!resolver->Resolve(true)) + _error->Discard(); +} + +- (void) popUpBook:(RVBook *)book { + [underlay_ popSubview:book]; +} + +- (CGRect) popUpBounds { + return [underlay_ bounds]; +} + +- (bool) perform { + if (![database_ prepare]) + return false; + + confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]]; + [confirm_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [confirm_ setDelegate:self]; + + ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]); + [page setDelegate:self]; + + [confirm_ setPage:page]; + [self popUpBook:confirm_]; + + return true; +} + +- (void) queue { + @synchronized (self) { + [self perform]; + } +} + +- (void) clearPackage:(Package *)package { + @synchronized (self) { + [package clear]; + [self resolve]; + [self perform]; + } +} + +- (void) installPackages:(NSArray *)packages { + @synchronized (self) { + for (Package *package in packages) + [package install]; + [self resolve]; + [self perform]; + } +} + +- (void) installPackage:(Package *)package { + @synchronized (self) { + [package install]; + [self resolve]; + [self perform]; + } +} + +- (void) removePackage:(Package *)package { + @synchronized (self) { + [package remove]; + [self resolve]; + [self perform]; + } +} + +- (void) distUpgrade { + @synchronized (self) { + if (![database_ upgrade]) + return; + [self perform]; + } +} + +- (void) cancel { + [self slideUp:[[[UIActionSheet alloc] + initWithTitle:nil + buttons:[NSArray arrayWithObjects:UCLocalize("CONTINUE_QUEUING"), UCLocalize("CANCEL_CLEAR"), nil] + defaultButtonIndex:1 + delegate:self + context:@"cancel" + ] autorelease]]; +} + +- (void) complete { + @synchronized (self) { + [self _reloadData]; + + if (confirm_ != nil) { + [confirm_ release]; + confirm_ = nil; + } + } +} + +- (void) confirm { + [overlay_ removeFromSuperview]; + reload_ = true; + + [progress_ + detachNewThreadSelector:@selector(perform) + toTarget:database_ + withObject:nil + title:UCLocalize("RUNNING") + ]; +} + +- (void) progressViewIsComplete:(ProgressView *)progress { + if (confirm_ != nil) { + [underlay_ addSubview:overlay_]; + [confirm_ popFromSuperviewAnimated:NO]; + } + + [self complete]; +} + +- (void) setPage:(RVPage *)page { + [page resetViewAnimated:NO]; + [page setDelegate:self]; + [book_ setPage:page]; +} + +- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class { + CydiaBrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease]; + [browser loadURL:url]; + return browser; +} + +- (SectionsView *) sectionsView { + if (sections_ == nil) + sections_ = [[SectionsView alloc] initWithBook:book_ database:database_]; + return sections_; +} + +- (ChangesView *) changesView { + if (changes_ == nil) + changes_ = [[ChangesView alloc] initWithBook:book_ database:database_ delegate:self]; + return changes_; +} + +- (ManageView *) manageView { + if (manage_ == nil) + manage_ = (ManageView *) [[self + _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]] + withClass:[ManageView class] + ] retain]; + return manage_; +} + +- (SearchView *) searchView { + if (search_ == nil) + search_ = [[SearchView alloc] initWithBook:book_ database:database_]; + return search_; +} + +- (void) tabBar:(UITabBar *)sender didSelectItem:(UITabBarItem *)item { + int tag = [item tag]; + if (tag == tag_) { + [book_ resetViewAnimated:YES]; + return; + } else if (tag_ == 1) + [[self sectionsView] resetView]; + + switch (tag) { + case 0: _setHomePage(self); break; + + case 1: [self setPage:[self sectionsView]]; break; + case 2: [self setPage:[self changesView]]; break; + case 3: [self setPage:[self manageView]]; break; + case 4: [self setPage:[self searchView]]; break; + + _nodefault + } + + tag_ = tag; +} + +- (void) askForSettings { + NSString *parenthetical(UCLocalize("PARENTHETICAL")); + + CYActionSheet *role([[[CYActionSheet alloc] + initWithTitle:UCLocalize("WHO_ARE_YOU") + buttons:[NSArray arrayWithObjects: + [NSString stringWithFormat:parenthetical, UCLocalize("USER"), UCLocalize("USER_EX")], + [NSString stringWithFormat:parenthetical, UCLocalize("HACKER"), UCLocalize("HACKER_EX")], + [NSString stringWithFormat:parenthetical, UCLocalize("DEVELOPER"), UCLocalize("DEVELOPER_EX")], + nil] + defaultButtonIndex:-1 + ] autorelease]); + + [role setBodyText:UCLocalize("ROLE_EX")]; + + int button([role yieldToPopupAlertAnimated:YES]); + + switch (button) { + case 1: Role_ = @"User"; break; + case 2: Role_ = @"Hacker"; break; + case 3: Role_ = @"Developer"; break; + + _nodefault + } + + Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys: + Role_, @"Role", + nil]; + + [Metadata_ setObject:Settings_ forKey:@"Settings"]; + + Changed_ = true; + + [role dismiss]; +} + +- (void) setPackageView:(PackageView *)view { + WebThreadLock(); + [view setPackage:nil]; +#if RecyclePackageViews + if ([details_ count] < 3) + [details_ addObject:view]; +#endif + WebThreadUnlock(); +} + +- (PackageView *) _packageView { + return [[[PackageView alloc] initWithBook:book_ database:database_] autorelease]; +} + +- (PackageView *) packageView { +#if RecyclePackageViews + PackageView *view; + size_t count([details_ count]); + + if (count == 0) { + view = [self _packageView]; + renew: + [details_ addObject:[self _packageView]]; + } else { + view = [[[details_ lastObject] retain] autorelease]; + [details_ removeLastObject]; + if (count == 1) + goto renew; + } + + return view; +#else + return [self _packageView]; +#endif +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"missing"]) + [sheet dismiss]; + else if ([context isEqualToString:@"cancel"]) { + bool clear; + + switch (button) { + case 1: + clear = false; + break; + + case 2: + clear = true; + break; + + _nodefault + } + + [sheet dismiss]; + + @synchronized (self) { + if (clear) + [self _reloadData]; + else { + Queuing_ = true; + [toolbar_ setBadgeValue:UCLocalize("Q_D") forButton:4]; + [book_ reloadData]; + } + + if (confirm_ != nil) { + [confirm_ release]; + confirm_ = nil; + } + } + } else if ([context isEqualToString:@"fixhalf"]) { + switch (button) { + case 1: + @synchronized (self) { + for (Package *broken in broken_) { + [broken remove]; + + NSString *id = [broken id]; + unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]); + unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]); + unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]); + unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]); + } + + [self resolve]; + [self perform]; + } + break; + + case 2: + [broken_ removeAllObjects]; + [self _loaded]; + break; + + _nodefault + } + + [sheet dismiss]; + } else if ([context isEqualToString:@"upgrade"]) { + switch (button) { + case 1: + @synchronized (self) { + for (Package *essential in essential_) + [essential install]; + + [self resolve]; + [self perform]; + } + break; + + case 2: + [self distUpgrade]; + break; + + case 3: + Ignored_ = YES; + break; + + _nodefault + } + + [sheet dismiss]; + } +} + +- (void) system:(NSString *)command { _pooled + system([command UTF8String]); +} + +- (void) applicationWillSuspend { + [database_ clean]; + [super applicationWillSuspend]; +} + +- (void) applicationSuspend:(__GSEvent *)event { + if (hud_ == nil && ![progress_ isRunning]) + [super applicationSuspend:event]; +} + +- (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 { + if (hud_ == nil) + [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3]; +} + +- (void) _setSuspended:(BOOL)value { + if (hud_ == nil) + [super _setSuspended:value]; +} + +- (UIProgressHUD *) addProgressHUD { + UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]); + [hud setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + + [window_ setUserInteractionEnabled:NO]; + [hud show:YES]; + [progress_ addSubview:hud]; + return hud; +} + +- (void) removeProgressHUD:(UIProgressHUD *)hud { + [hud show:NO]; + [hud removeFromSuperview]; + [window_ setUserInteractionEnabled:YES]; +} + +- (RVPage *) pageForPackage:(NSString *)name { + if (Package *package = [database_ packageWithName:name]) { + PackageView *view([self packageView]); + [view setPackage:package]; + return view; + } else { + NSURL *url([NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"unknown" ofType:@"html"]]); + url = [NSURL URLWithString:[[url absoluteString] stringByAppendingString:[NSString stringWithFormat:@"?%@", name]]]; + return [self _pageForURL:url withClass:[CydiaBrowserView class]]; + } +} + +- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag { + if (tag != NULL) + *tag = -1; + + NSString *href([url absoluteString]); + if ([href hasPrefix:@"apptapp://package/"]) + return [self pageForPackage:[href substringFromIndex:18]]; + + NSString *scheme([[url scheme] lowercaseString]); + if (![scheme isEqualToString:@"cydia"]) + return nil; + NSString *path([url absoluteString]); + if ([path length] < 8) + return nil; + path = [path substringFromIndex:8]; + if (![path hasPrefix:@"/"]) + path = [@"/" stringByAppendingString:path]; + + if ([path isEqualToString:@"/add-source"]) + return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease]; + else if ([path isEqualToString:@"/storage"]) + return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[CydiaBrowserView class]]; + else if ([path isEqualToString:@"/sources"]) + return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease]; + else if ([path isEqualToString:@"/packages"]) + return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease]; + else if ([path hasPrefix:@"/url/"]) + return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[CydiaBrowserView class]]; + else if ([path hasPrefix:@"/launch/"]) + [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO]; + else if ([path hasPrefix:@"/package-settings/"]) + return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease]; + else if ([path hasPrefix:@"/package-signature/"]) + return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease]; + else if ([path hasPrefix:@"/package/"]) + return [self pageForPackage:[path substringFromIndex:9]]; + else if ([path hasPrefix:@"/files/"]) { + NSString *name = [path substringFromIndex:7]; + + if (Package *package = [database_ packageWithName:name]) { + FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease]; + [files setPackage:package]; + return files; + } + } + + return nil; +} + +- (void) applicationOpenURL:(NSURL *)url { + [super applicationOpenURL:url]; + int tag; + if (RVPage *page = [self pageForURL:url hasTag:&tag]) { + [self setPage:page]; + tag_ = tag; + [toolbar_ setSelectedItem:(tag_ == -1 ? nil : [items_ objectAtIndex:tag_])]; + } +} + +- (void) applicationDidFinishLaunching:(id)unused { + [BrowserView _initialize]; + + [NSURLProtocol registerClass:[CydiaURLProtocol class]]; + + Font12_ = [[UIFont systemFontOfSize:12] retain]; + Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain]; + Font14_ = [[UIFont systemFontOfSize:14] retain]; + Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain]; + Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain]; + + tag_ = 0; + + essential_ = [[NSMutableArray alloc] initWithCapacity:4]; + broken_ = [[NSMutableArray alloc] initWithCapacity:4]; + + UIScreen *screen([UIScreen mainScreen]); + + window_ = [[UIWindow alloc] initWithFrame:[screen bounds]]; + [window_ orderFront:self]; + [window_ makeKey:self]; + [window_ setHidden:NO]; + + root_ = [[CydiaViewController alloc] init]; + [window_ addSubview:[root_ view]]; + + database_ = [Database sharedInstance]; + + progress_ = [[ProgressView alloc] initWithFrame:[[root_ view] bounds] database:database_ delegate:self]; + [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [[root_ view] addSubview:progress_]; + + [database_ setDelegate:progress_]; + + underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]]; + [underlay_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [progress_ setContentView:underlay_]; + + [progress_ resetView]; + + if ( + readlink("/Applications", NULL, 0) == -1 && errno == EINVAL || + readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL || + readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL || + //readlink("/usr/bin", NULL, 0) == -1 && errno == EINVAL || + readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL || + readlink("/usr/lib/pam", NULL, 0) == -1 && errno == EINVAL || + readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL || + readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL || + //readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL || + false + ) { + [self setIdleTimerDisabled:YES]; + + hud_ = [self addProgressHUD]; + [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"]; + [self setStatusBarShowsProgress:YES]; + + [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"]; + + [self setStatusBarShowsProgress:NO]; + [self removeProgressHUD:hud_]; + hud_ = nil; + + if (ExecFork() == 0) { + execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL); + perror("launchctl stop"); + } + + return; + } + + if (Role_ == nil) + [self askForSettings]; + + _trace(); + overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]]; + [overlay_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + + CGRect screenrect = [UIHardware fullScreenApplicationContentRect]; + + book_ = [[CYBook alloc] initWithFrame:CGRectMake( + 0, 0, screenrect.size.width, screenrect.size.height - 48 + ) database:database_]; + + [book_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth]; + [overlay_ addSubview:book_]; + + [book_ setDelegate:self]; + + items_ = [[NSArray arrayWithObjects: + [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:0] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("SECTIONS") image:[UIImage applicationImageNamed:@"install.png"] tag:1] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:2] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("MANAGE") image:[UIImage applicationImageNamed:@"manage.png"] tag:3] autorelease], + [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:4] autorelease], + nil] retain]; + + toolbar_ = [[UITabBar alloc] + initWithFrame:CGRectMake( + 0, screenrect.size.height - ButtonBarHeight_, + screenrect.size.width, ButtonBarHeight_ + ) + ]; + + [toolbar_ setItems:items_]; + + [toolbar_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)]; + [overlay_ addSubview:toolbar_]; + + [toolbar_ setDelegate:self]; + + /*int buttons[5] = {1, 2, 3, 4, 5}; + [toolbar_ registerButtonGroup:0 withButtons:buttons withCount:5]; + [toolbar_ showButtonGroup:0 withDuration:0]; + + for (int i = 0; i != 5; ++i) { + UIView *button([toolbar_ viewWithTag:(i + 1)]); + + [button setFrame:CGRectMake( + i * (screenrect.size.width / 5) + (screenrect.size.width / 5 - ButtonBarWidth_) / 2, 1, + ButtonBarWidth_, ButtonBarHeight_ + )]; + + [button setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin]; + }*/ + + [toolbar_ setSelectedItem:[items_ objectAtIndex:0]]; + + [UIKeyboard initImplementationNow]; + /*CGSize keysize = [UIKeyboard defaultSize]; + CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize}; + keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect]; + [overlay_ addSubview:keyboard_];*/ + + [underlay_ addSubview:overlay_]; + + [self reloadData]; + +#if RecyclePackageViews + details_ = [[NSMutableArray alloc] initWithCapacity:4]; + [details_ addObject:[self _packageView]]; + [details_ addObject:[self _packageView]]; +#endif + + PrintTimes(); + + _setHomePage(self); +} + +- (void) showKeyboard:(BOOL)show { + CGSize keysize([UIKeyboard defaultSize]); + CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize}; + CGRect keyup(keydown); + keyup.origin.y -= keysize.height; + + UIFrameAnimation *animation([[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease]); + [animation setSignificantRectFields:2]; + + if (show) { + [animation setStartFrame:keydown]; + [animation setEndFrame:keyup]; + [keyboard_ activate]; + } else { + [animation setStartFrame:keyup]; + [animation setEndFrame:keydown]; + [keyboard_ deactivate]; + } + + [[UIAnimator sharedAnimator] + addAnimations:[NSArray arrayWithObjects:animation, nil] + withDuration:KeyboardTime_ + start:YES + ]; +} + +- (void) slideUp:(UIActionSheet *)alert { + [alert setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [alert presentSheetInView:overlay_]; +} + +@end + +/*IMP alloc_; +id Alloc_(id self, SEL selector) { + id object = alloc_(self, selector); + lprintf("[%s]A-%p\n", self->isa->name, object); + return object; +}*/ + +/*IMP dealloc_; +id Dealloc_(id self, SEL selector) { + id object = dealloc_(self, selector); + lprintf("[%s]D-%p\n", self->isa->name, object); + return object; +}*/ + +Class $WebDefaultUIKitDelegate; + +MSHook(void, UIWebDocumentView$_setUIKitDelegate$, UIWebDocumentView *self, SEL _cmd, id delegate) { + if (delegate == nil && $WebDefaultUIKitDelegate != nil) + delegate = [$WebDefaultUIKitDelegate sharedUIKitDelegate]; + return _UIWebDocumentView$_setUIKitDelegate$(self, _cmd, delegate); +} + +int main(int argc, char *argv[]) { _pooled + _trace(); + + if (Class $UIDevice = objc_getClass("UIDevice")) { + UIDevice *device([$UIDevice currentDevice]); + IsWildcat_ = [device respondsToSelector:@selector(isWildcat)] && [device isWildcat]; + } else + IsWildcat_ = false; + + PackageName = reinterpret_cast(method_getImplementation(class_getInstanceMethod([Package class], @selector(cyname)))); + + /* Library Hacks {{{ */ + class_addMethod(objc_getClass("WebScriptObject"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &WebScriptObject$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16"); + class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16"); + + $WebDefaultUIKitDelegate = objc_getClass("WebDefaultUIKitDelegate"); + Method UIWebDocumentView$_setUIKitDelegate$(class_getInstanceMethod([WebView class], @selector(_setUIKitDelegate:))); + if (UIWebDocumentView$_setUIKitDelegate$ != NULL) { + _UIWebDocumentView$_setUIKitDelegate$ = reinterpret_cast(method_getImplementation(UIWebDocumentView$_setUIKitDelegate$)); + method_setImplementation(UIWebDocumentView$_setUIKitDelegate$, reinterpret_cast(&$UIWebDocumentView$_setUIKitDelegate$)); + } + /* }}} */ + /* Set Locale {{{ */ + Locale_ = CFLocaleCopyCurrent(); + Languages_ = [NSLocale preferredLanguages]; + //CFStringRef locale(CFLocaleGetIdentifier(Locale_)); + //NSLog(@"%@", [Languages_ description]); + + const char *lang; + if (Languages_ == nil || [Languages_ count] == 0) + // XXX: consider just setting to C and then falling through? + lang = NULL; + else { + lang = [[Languages_ objectAtIndex:0] UTF8String]; + setenv("LANG", lang, true); + } + + //std::setlocale(LC_ALL, lang); + NSLog(@"Setting Language: %s", lang); + /* }}} */ + + apr_app_initialize(&argc, const_cast(&argv), NULL); + + /* Parse Arguments {{{ */ + bool substrate(false); + + if (argc != 0) { + char **args(argv); + int arge(1); + + for (int argi(1); argi != argc; ++argi) + if (strcmp(argv[argi], "--") == 0) { + arge = argi; + argv[argi] = argv[0]; + argv += argi; + argc -= argi; + break; + } + + for (int argi(1); argi != arge; ++argi) + if (strcmp(args[argi], "--substrate") == 0) + substrate = true; + else + fprintf(stderr, "unknown argument: %s\n", args[argi]); + } + /* }}} */ + + App_ = [[NSBundle mainBundle] bundlePath]; + Home_ = NSHomeDirectory(); + Advanced_ = YES; + + setuid(0); + setgid(0); + + /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc)); + alloc_ = alloc->method_imp; + alloc->method_imp = (IMP) &Alloc_;*/ + + /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc)); + dealloc_ = dealloc->method_imp; + dealloc->method_imp = (IMP) &Dealloc_;*/ + + /* System Information {{{ */ + size_t size; + + int maxproc; + size = sizeof(maxproc); + if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1) + perror("sysctlbyname(\"kern.maxproc\", ?)"); + else if (maxproc < 64) { + maxproc = 64; + if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1) + perror("sysctlbyname(\"kern.maxproc\", #)"); + } + + sysctlbyname("kern.osversion", NULL, &size, NULL, 0); + char *osversion = new char[size]; + if (sysctlbyname("kern.osversion", osversion, &size, NULL, 0) == -1) + perror("sysctlbyname(\"kern.osversion\", ?)"); + else + System_ = [NSString stringWithUTF8String:osversion]; + + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char *machine = new char[size]; + if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1) + perror("sysctlbyname(\"hw.machine\", ?)"); + else + Machine_ = machine; + + if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice")) { + if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) { + if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) { + SerialNumber_ = [NSString stringWithString:(NSString *)serial]; + CFRelease(serial); + } + + if (CFTypeRef ecid = IORegistryEntrySearchCFProperty(service, kIODeviceTreePlane, CFSTR("unique-chip-id"), kCFAllocatorDefault, kIORegistryIterateRecursively)) { + NSData *data((NSData *) ecid); + size_t length([data length]); + uint8_t bytes[length]; + [data getBytes:bytes]; + char string[length * 2 + 1]; + for (size_t i(0); i != length; ++i) + sprintf(string + i * 2, "%.2X", bytes[length - i - 1]); + ChipID_ = [NSString stringWithUTF8String:string]; + CFRelease(ecid); + } + + IOObjectRelease(service); + } + } + + UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier]; + + if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) + Build_ = [system objectForKey:@"ProductBuildVersion"]; + if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) { + Product_ = [info objectForKey:@"SafariProductVersion"]; + Safari_ = [info objectForKey:@"CFBundleVersion"]; + } + /* }}} */ + /* Load Database {{{ */ + _trace(); + Metadata_ = [[[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"] autorelease]; + _trace(); + SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease]; + _trace(); + + if (Metadata_ == NULL) + Metadata_ = [NSMutableDictionary dictionaryWithCapacity:2]; + else { + Settings_ = [Metadata_ objectForKey:@"Settings"]; + + Packages_ = [Metadata_ objectForKey:@"Packages"]; + Sections_ = [Metadata_ objectForKey:@"Sections"]; + Sources_ = [Metadata_ objectForKey:@"Sources"]; + + Token_ = [Metadata_ objectForKey:@"Token"]; + } + + if (Settings_ != nil) + Role_ = [Settings_ objectForKey:@"Role"]; + + if (Packages_ == nil) { + Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease]; + [Metadata_ setObject:Packages_ forKey:@"Packages"]; + } + + if (Sections_ == nil) { + Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease]; + [Metadata_ setObject:Sections_ forKey:@"Sections"]; + } + + if (Sources_ == nil) { + Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease]; + [Metadata_ setObject:Sources_ forKey:@"Sources"]; + } + /* }}} */ + +#if RecycleWebViews + Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease]; +#endif + + Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil]; + + if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", F_OK) == 0) + dlopen("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", RTLD_LAZY | RTLD_GLOBAL); + if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0) + dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL); + /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0) + dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/ + + int version([[NSString stringWithContentsOfFile:@"/var/lib/cydia/firmware.ver"] intValue]); + + if (access("/tmp/.cydia.fw", F_OK) == 0) { + unlink("/tmp/.cydia.fw"); + goto firmware; + } else if (access("/User", F_OK) != 0 || version < 2) { + firmware: + _trace(); + system("/usr/libexec/cydia/firmware.sh"); + _trace(); + } + + _assert([[NSFileManager defaultManager] + createDirectoryAtPath:@"/var/cache/apt/archives/partial" + withIntermediateDirectories:YES + attributes:nil + error:NULL + ]); + + if (access("/tmp/cydia.chk", F_OK) == 0) { + if (unlink("/var/cache/apt/pkgcache.bin") == -1) + _assert(errno == ENOENT); + if (unlink("/var/cache/apt/srcpkgcache.bin") == -1) + _assert(errno == ENOENT); + } + + /* APT Initialization {{{ */ + _assert(pkgInitConfig(*_config)); + _assert(pkgInitSystem(*_config, _system)); + + if (lang != NULL) + _config->Set("APT::Acquire::Translation", lang); + _config->Set("Acquire::http::Timeout", 15); + _config->Set("Acquire::http::MaxParallel", 3); + /* }}} */ + /* Color Choices {{{ */ + space_ = CGColorSpaceCreateDeviceRGB(); + + Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0); + Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0); + Black_.Set(space_, 0.0, 0.0, 0.0, 1.0); + Off_.Set(space_, 0.9, 0.9, 0.9, 1.0); + White_.Set(space_, 1.0, 1.0, 1.0, 1.0); + Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0); + Green_.Set(space_, 0.0, 0.5, 0.0, 1.0); + Purple_.Set(space_, 0.0, 0.0, 0.7, 1.0); + Purplish_.Set(space_, 0.4, 0.4, 0.8, 1.0); + + InstallingColor_ = [UIColor colorWithRed:0.88f green:1.00f blue:0.88f alpha:1.00f]; + RemovingColor_ = [UIColor colorWithRed:1.00f green:0.88f blue:0.88f alpha:1.00f]; + /* }}}*/ + /* UIKit Configuration {{{ */ + void (*$GSFontSetUseLegacyFontMetrics)(BOOL)(reinterpret_cast(dlsym(RTLD_DEFAULT, "GSFontSetUseLegacyFontMetrics"))); + if ($GSFontSetUseLegacyFontMetrics != NULL) + $GSFontSetUseLegacyFontMetrics(YES); + + // XXX: I have a feeling this was important + //UIKeyboardDisableAutomaticAppearance(); + /* }}} */ + + Colon_ = UCLocalize("COLON_DELIMITED"); + Error_ = UCLocalize("ERROR"); + Warning_ = UCLocalize("WARNING"); + + _trace(); + int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia"); + + CGColorSpaceRelease(space_); + CFRelease(Locale_); + + return value; +} diff --git a/CydiaSettings.bundle/icon.png b/CydiaSettings.bundle/icon.png new file mode 100644 index 0000000..c42b65b Binary files /dev/null and b/CydiaSettings.bundle/icon.png differ diff --git a/LaunchDaemons/com.saurik.Cydia.Startup.plist b/LaunchDaemons/com.saurik.Cydia.Startup.plist new file mode 100644 index 0000000..0a94d2e --- /dev/null +++ b/LaunchDaemons/com.saurik.Cydia.Startup.plist @@ -0,0 +1,12 @@ + + + + + Label + com.saurik.Cydia.Startup + Program + /usr/libexec/cydia/startup + RunAtLoad + + + diff --git a/Library/asuser b/Library/asuser new file mode 100755 index 0000000..a4285a6 --- /dev/null +++ b/Library/asuser @@ -0,0 +1,11 @@ +#!/bin/bash + +version=$(sw_vers -productVersion) + +if [[ ${version} = 1.0* || ${version} = 1.1.[012] ]]; then + user=root +else + user=mobile +fi + +su -c "${user}" "$@" diff --git a/Library/finish.sh b/Library/finish.sh new file mode 100755 index 0000000..2cdbb2e --- /dev/null +++ b/Library/finish.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +declare -a cydia +cydia=($CYDIA) + +if [[ ${CYDIA+@} ]]; then + eval "echo 'finish:$1' >&${cydia[0]}" +fi diff --git a/Library/firmware.sh b/Library/firmware.sh new file mode 100755 index 0000000..d2c0488 --- /dev/null +++ b/Library/firmware.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +set -e +shopt -s extglob + +version=$(sw_vers -productVersion) + +cat /var/lib/dpkg/status | { + +while IFS= read -r line; do + #echo "#${firmware+@}/${blank+@} ${line}" 1>&2 + + if [[ ${line} == '' && "${blank+@}" ]]; then + continue + else + unset blank + fi + + if [[ ${line} == "Package: "@(firmware|gsc.*) ]]; then + firmware= + elif [[ ${line} == '' ]]; then + blank= + fi + + if [[ "${firmware+@}" ]]; then + if [[ "${blank+@}" ]]; then + unset firmware + fi + continue + fi + + #echo "${firmware+@}/${blank+@} ${line}" 1>&2 + echo "${line}" +done + +#echo "#${firmware+@}/${blank+@} EOF" 1>&2 +if ! [[ "${blank+@}" || "${firmware+@}" ]]; then + echo +fi + +cat </var/lib/dpkg/info/firmware.list + +gssc 2>&1 | sed -re ' + /^ [^ ]* = [0-9.]*;$/ ! d; + s/^ ([^ ]*) = ([0-9.]*);$/\1 \2/; + s/([A-Z])/-\L\1/g; s/^"([^ ]*)"/\1/; + s/^-//; + / 0$/ d; +' | while read -r name value; do + +cat </var/lib/dpkg/info/gsc."${name}".list + +done + +} >/var/lib/dpkg/status_ + +mv -f /var/lib/dpkg/status{_,} + +if [[ ${version} = 1.0* || ${version} = 1.1.[012] ]]; then + user=root +else + user=mobile +fi + +if [[ ! -h /User && -d /User ]]; then + cp -afT /User /var/"${user}" +fi && rm -rf /User && ln -s "/var/${user}" /User + +echo 2 >/var/lib/cydia/firmware.ver diff --git a/Library/free.sh b/Library/free.sh new file mode 100755 index 0000000..2e90608 --- /dev/null +++ b/Library/free.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# /usr/bin \ + +for dir in \ + /Applications \ + /Library/Wallpaper \ + /Library/Ringtones \ + /usr/include \ + /usr/lib/pam \ + /usr/libexec \ + /usr/share \ +; do + . /usr/libexec/cydia/move.sh "$@" "${dir}" +done + +sync diff --git a/Library/move.sh b/Library/move.sh new file mode 100755 index 0000000..4a15ca7 --- /dev/null +++ b/Library/move.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +shopt -s extglob nullglob + +if [[ ${1:0:1} == - ]]; then + v=$1 + shift 1 +else + v= +fi + +function df_() { + free=$(df -B1 "$1") + free=${free% *%*} + free=${free%%*( )} + free=${free##* } + echo "${free}" +} + +function mv_() { + src=$1 + + mkdir -p /var/stash + dst=$(mktemp -d /var/stash/"${src##*/}".XXXXXX) + + if [[ -e ${src} ]]; then + chmod --reference="${src}" "${dst}" + chown --reference="${src}" "${dst}" + + cp -aT $v "${src}" "${dst}" || { + rm -rf "${dst}" + exit 1 + } + + rm -rf $v "${src}" + else + chmod 775 "${dst}" + chown root.admin "${dst}" + fi + + ln -s "${dst}" "${src}" +} + +function shift_() { + dir=${1%/} + + if [[ -d ${dir} && ! -h ${dir} ]]; then + used=$(/usr/libexec/cydia/du -bs "${dir}") + used=${used%%$'\t'*} + free=$(df_ /var) + + if [[ $((used + 524288)) -lt ${free} ]]; then + mv_ "${dir}" + fi + elif [[ ! -e ${dir} ]]; then + rm -f "${dir}" + mv_ "${dir}" + fi +} + +shift_ "$@" diff --git a/Library/space.sh b/Library/space.sh new file mode 100755 index 0000000..3be1f0d --- /dev/null +++ b/Library/space.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +for dir in \ + /Applications \ + /Library/Ringtones \ + /Library/Wallpaper \ + /System/Library/Fonts \ + /System/Library/TextInput \ + /usr/share +do + . /usr/libexec/cydia/move.sh "$@" "${dir}" +done diff --git a/Library/startup b/Library/startup new file mode 100755 index 0000000..4d72c33 --- /dev/null +++ b/Library/startup @@ -0,0 +1,21 @@ +#!/bin/bash + +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin +sbdidlaunch + +if [[ ! -e /var/lib/cydia/firmware.ver ]]; then + cache= +fi + +/usr/libexec/cydia/firmware.sh + +debs=(/var/root/Media/Cydia/AutoInstall/*.deb) +if [[ ${#debs[@]} -ne 0 && -f ${debs[0]} ]]; then + dpkg -i "${debs[@]}" + rm -f "${debs[@]}" + cache= +fi + +if [[ ${cache+@} ]]; then + su -c uicache mobile +fi diff --git a/Preferences.mm b/Preferences.mm new file mode 100644 index 0000000..f576193 --- /dev/null +++ b/Preferences.mm @@ -0,0 +1,438 @@ +/* Source {{{ */ +@interface Source : NSObject { + NSString *description_; + NSString *label_; + NSString *origin_; + + NSString *uri_; + NSString *distribution_; + NSString *type_; + + BOOL trusted_; +} + +- (void) dealloc; + +- (Source *) initWithMetaIndex:(metaIndex *)index; + +- (BOOL) trusted; + +- (NSString *) uri; +- (NSString *) distribution; +- (NSString *) type; + +- (NSString *) description; +- (NSString *) label; +- (NSString *) origin; +@end + +@implementation Source + +- (void) dealloc { + [uri_ release]; + [distribution_ release]; + [type_ release]; + + if (description_ != nil) + [description_ release]; + if (label_ != nil) + [label_ release]; + if (origin_ != nil) + [origin_ release]; + + [super dealloc]; +} + +- (Source *) initWithMetaIndex:(metaIndex *)index { + if ((self = [super init]) != nil) { + trusted_ = index->IsTrusted(); + + uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain]; + distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain]; + type_ = [[NSString stringWithCString:index->GetType()] retain]; + + description_ = nil; + label_ = nil; + origin_ = nil; + + debReleaseIndex *dindex(dynamic_cast(index)); + if (dindex != NULL) { + std::ifstream release(dindex->MetaIndexFile("Release").c_str()); + std::string line; + while (std::getline(release, line)) { + std::string::size_type colon(line.find(':')); + if (colon == std::string::npos) + continue; + + std::string name(line.substr(0, colon)); + std::string value(line.substr(colon + 1)); + while (!value.empty() && value[0] == ' ') + value = value.substr(1); + + if (name == "Description") + description_ = [[NSString stringWithCString:value.c_str()] retain]; + else if (name == "Label") + label_ = [[NSString stringWithCString:value.c_str()] retain]; + else if (name == "Origin") + origin_ = [[NSString stringWithCString:value.c_str()] retain]; + } + } + } return self; +} + +- (BOOL) trusted { + return trusted_; +} + +- (NSString *) uri { + return uri_; +} + +- (NSString *) distribution { + return distribution_; +} + +- (NSString *) type { + return type_; +} + +- (NSString *) description { + return description_; +} + +- (NSString *) label { + return label_; +} + +- (NSString *) origin { + return origin_; +} + +@end +/* }}} */ +/* Source Cell {{{ */ +@interface SourceCell : UITableCell { + UITextLabel *description_; + UIRightTextLabel *label_; + UITextLabel *origin_; +} + +- (void) dealloc; + +- (SourceCell *) initWithSource:(Source *)source; + +- (void) _setSelected:(float)fraction; +- (void) setSelected:(BOOL)selected; +- (void) setSelected:(BOOL)selected withFade:(BOOL)fade; +- (void) _setSelectionFadeFraction:(float)fraction; + +@end + +@implementation SourceCell + +- (void) dealloc { + [description_ release]; + [label_ release]; + [origin_ release]; + [super dealloc]; +} + +- (SourceCell *) initWithSource:(Source *)source { + if ((self = [super init]) != nil) { + GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20); + GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14); + + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + float clear[] = {0, 0, 0, 0}; + + NSString *description = [source description]; + if (description == nil) + description = [source uri]; + + description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 270, 25)]; + [description_ setBackgroundColor:CGColorCreate(space, clear)]; + [description_ setFont:bold]; + [description_ setText:description]; + + NSString *label = [source label]; + if (label == nil) + label = [source type]; + + label_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 32, 90, 25)]; + [label_ setBackgroundColor:CGColorCreate(space, clear)]; + [label_ setFont:small]; + [label_ setText:label]; + + NSString *origin = [source origin]; + if (origin == nil) + origin = [source distribution]; + + origin_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)]; + [origin_ setBackgroundColor:CGColorCreate(space, clear)]; + [origin_ setFont:small]; + [origin_ setText:origin]; + + [self addSubview:description_]; + [self addSubview:label_]; + [self addSubview:origin_]; + + CFRelease(small); + CFRelease(bold); + } return self; +} + +- (void) _setSelected:(float)fraction { + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + + float black[] = { + interpolate(0.0, 1.0, fraction), + interpolate(0.0, 1.0, fraction), + interpolate(0.0, 1.0, fraction), + 1.0}; + + float blue[] = { + interpolate(0.2, 1.0, fraction), + interpolate(0.2, 1.0, fraction), + interpolate(1.0, 1.0, fraction), + 1.0}; + + float gray[] = { + interpolate(0.4, 1.0, fraction), + interpolate(0.4, 1.0, fraction), + interpolate(0.4, 1.0, fraction), + 1.0}; + + [description_ setColor:CGColorCreate(space, black)]; + [label_ setColor:CGColorCreate(space, blue)]; + [origin_ setColor:CGColorCreate(space, gray)]; +} + +- (void) setSelected:(BOOL)selected { + [self _setSelected:(selected ? 1.0 : 0.0)]; + [super setSelected:selected]; +} + +- (void) setSelected:(BOOL)selected withFade:(BOOL)fade { + if (!fade) + [self _setSelected:(selected ? 1.0 : 0.0)]; + [super setSelected:selected withFade:fade]; +} + +- (void) _setSelectionFadeFraction:(float)fraction { + [self _setSelected:fraction]; + [super _setSelectionFadeFraction:fraction]; +} + +@end +/* }}} */ + +/* Sources View {{{ */ +@interface SourcesView : UIView { + UISectionList *list_; + Database *database_; + id delegate_; + NSMutableArray *sources_; + UIActionSheet *alert_; +} + +- (int) numberOfSectionsInSectionList:(UISectionList *)list; +- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section; +- (int) sectionList:(UISectionList *)list rowForSection:(int)section; + +- (int) numberOfRowsInTable:(UITable *)table; +- (float) table:(UITable *)table heightForRow:(int)row; +- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col; +- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row; +- (void) tableRowSelected:(NSNotification*)notification; + +- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button; + +- (void) dealloc; +- (id) initWithFrame:(CGRect)frame database:(Database *)database; +- (void) setDelegate:(id)delegate; +- (void) reloadData; +- (NSString *) leftTitle; +- (NSString *) rightTitle; +@end + +@implementation SourcesView + +- (int) numberOfSectionsInSectionList:(UISectionList *)list { + return 1; +} + +- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section { + return @"sources"; +} + +- (int) sectionList:(UISectionList *)list rowForSection:(int)section { + return 0; +} + +- (int) numberOfRowsInTable:(UITable *)table { + return [sources_ count]; +} + +- (float) table:(UITable *)table heightForRow:(int)row { + return 64; +} + +- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col { + return [[[SourceCell alloc] initWithSource:[sources_ objectAtIndex:row]] autorelease]; +} + +- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row { + return NO; +} + +- (void) tableRowSelected:(NSNotification*)notification { + UITable *table([list_ table]); + int row([table selectedRow]); + if (row == INT_MAX) + return; + + [table selectRow:-1 byExtendingSelection:NO withFade:YES]; +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + [alert_ dismiss]; + [alert_ release]; + alert_ = nil; +} + +- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button { + switch (button) { + case 0: + alert_ = [[UIActionSheet alloc] + initWithTitle:@"Unimplemented" + buttons:[NSArray arrayWithObjects:@"Okay", nil] + defaultButtonIndex:0 + delegate:self + context:self + ]; + + [alert_ setBodyText:@"This feature will be implemented soon. In the mean time, you may add sources by adding .list files to '/etc/apt/sources.list.d'. If you'd like to be in the default list, please contact the author of Packager."]; + [alert_ popupAlertAnimated:YES]; + break; + + case 1: + [delegate_ update]; + break; + } +} + +- (void) dealloc { + if (sources_ != nil) + [sources_ release]; + [list_ release]; + [super dealloc]; +} + +- (id) initWithFrame:(CGRect)frame database:(Database *)database { + if ((self = [super initWithFrame:frame]) != nil) { + database_ = database; + sources_ = nil; + + CGSize navsize = [UINavigationBar defaultSize]; + CGRect navrect = {{0, 0}, navsize}; + CGRect bounds = [self bounds]; + + navbar_ = [[UINavigationBar alloc] initWithFrame:navrect]; + [self addSubview:navbar_]; + + [navbar_ setBarStyle:1]; + [navbar_ setDelegate:self]; + + UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Sources"] autorelease]; + [navbar_ pushNavigationItem:navitem]; + + list_ = [[UISectionList alloc] initWithFrame:CGRectMake( + 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height + )]; + + [self addSubview:list_]; + + [list_ setDataSource:self]; + [list_ setShouldHideHeaderInShortLists:NO]; + + UITableColumn *column = [[UITableColumn alloc] + initWithTitle:@"Name" + identifier:@"name" + width:frame.size.width + ]; + + UITable *table = [list_ table]; + [table setSeparatorStyle:1]; + [table addTableColumn:column]; + [table setDelegate:self]; + } return self; +} + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + +- (void) reloadData { + pkgSourceList list; + _assert(list.ReadMainList()); + + if (sources_ != nil) + [sources_ release]; + + sources_ = [[NSMutableArray arrayWithCapacity:16] retain]; + for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source) + [sources_ addObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]]; + + [list_ reloadData]; +} + +- (NSString *) leftTitle { + return @"Refresh All"; +} + +- (NSString *) rightTitle { + return @"Edit"; +} + +@end +/* }}} */ +/* Settings View {{{ */ +@interface SettingsView : ResetView { +} + +- (void) dealloc; +- (void) reloadData; +@end + +@implementation SettingsView + +- (void) dealloc { + [super dealloc]; +} + +- (id) initWithFrame:(CGRect)frame database:(Database *)database { + if ((self = [super initWithFrame:frame]) != nil) { + database_ = database; + sources_ = nil; + + CGSize navsize = [UINavigationBar defaultSize]; + CGRect navrect = {{0, 0}, navsize}; + CGRect bounds = [self bounds]; + + navbar_ = [[UINavigationBar alloc] initWithFrame:navrect]; + [self addSubview:navbar_]; + + [navbar_ setBarStyle:1]; + [navbar_ setDelegate:self]; + + UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Settings"] autorelease]; + [navbar_ pushNavigationItem:navitem]; + } return self; +} + +- (void) reloadData { + [self resetView]; +} + +@end +/* }}} */ diff --git a/UICaboodle/BrowserView.h b/UICaboodle/BrowserView.h new file mode 100644 index 0000000..4d30ef7 --- /dev/null +++ b/UICaboodle/BrowserView.h @@ -0,0 +1,124 @@ +#import "ResetView.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#import +#import + +#include +#include + +#import + +@class NSMutableArray; +@class NSString; +@class NSURL; +@class NSURLRequest; + +@class UIProgressIndicator; +@class UIScroller; +@class UIDocumentWebView; + +@class WebView; + +@class Database; +@class IndirectDelegate; + +@interface WebScriptObject (UICaboodle) +- (unsigned) count; +- (id) objectAtIndex:(unsigned)index; +@end + +@protocol BrowserViewDelegate +- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag; +@end + +@interface BrowserView : RVPage < + RVBookHook +> { + UIScroller *scroller_; + UIWebDocumentView *document_; + UIProgressIndicator *indicator_; + IndirectDelegate *indirect_; + NSURLAuthenticationChallenge *challenge_; + + bool error_; + NSURLRequest *request_; + + NSNumber *confirm_; + NSNumber *sensitive_; + NSString *title_; + NSMutableSet *loading_; + bool reloading_; + + NSString *button_; + NSString *style_; + + WebScriptObject *function_; + WebScriptObject *closer_; + WebScriptObject *special_; + WebScriptObject *finish_; + + bool pushed_; + + float width_; + bool popup_; + + CGSize size_; + bool editing_; + + Class class_; +} + ++ (void) _initialize; + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button; + +- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy; +- (void) loadURL:(NSURL *)url; + +- (void) loadRequest:(NSURLRequest *)request; +- (void) reloadURL; +- (bool) isLoading; + +- (void) fixScroller; + +- (WebView *) webView; +- (UIWebDocumentView *) documentView; + +- (id) initWithBook:(RVBook *)book; +- (id) initWithBook:(RVBook *)book forWidth:(float)width; +- (id) initWithBook:(RVBook *)book forWidth:(float)width ofClass:(Class)_class; + +- (NSString *) stringByEvaluatingJavaScriptFromString:(NSString *)script; +- (void) callFunction:(WebScriptObject *)function; + +- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame; +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame; + +- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source; + ++ (float) defaultWidth; +- (void) setViewportWidth:(float)width; + +- (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function; +- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function; +- (void) setFinishHook:(id)function; +- (void) setPopupHook:(id)function; + +- (id) _rightButtonTitle; + +- (bool) promptForSensitive:(NSString *)name; +- (bool) allowSensitiveRequests; + +@end diff --git a/UICaboodle/BrowserView.mm b/UICaboodle/BrowserView.mm new file mode 100644 index 0000000..caa2ba2 --- /dev/null +++ b/UICaboodle/BrowserView.mm @@ -0,0 +1,1527 @@ +#include +#include + +#import +// XXX: fix the minimum requirement +extern NSString * const kCAFilterNearest; + +#include +#include + +#include "substrate.h" + +#define ForSaurik 0 + +static bool Wildcat_; + +static CFArrayRef (*$GSSystemCopyCapability)(CFStringRef); +static CFArrayRef (*$GSSystemGetCapability)(CFStringRef); +static Class $UIFormAssistant; +static Class $UIWebBrowserView; + +@interface NSString (UIKit) +- (NSString *) stringByAddingPercentEscapes; +@end + +/* Indirect Delegate {{{ */ +@interface IndirectDelegate : NSObject { + _transient volatile id delegate_; +} + +- (void) setDelegate:(id)delegate; +- (id) initWithDelegate:(id)delegate; +@end + +@implementation IndirectDelegate + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + +- (id) initWithDelegate:(id)delegate { + delegate_ = delegate; + return self; +} + +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didClearWindowObject:window forFrame:frame]; +} + +- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didCommitLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didFailLoadWithError:error forFrame:frame]; +} + +- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didFailProvisionalLoadWithError:error forFrame:frame]; +} + +- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didFinishLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didReceiveTitle:title forFrame:frame]; +} + +- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didStartProvisionalLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source { + if (delegate_ != nil) + return [delegate_ webView:sender resource:identifier didReceiveAuthenticationChallenge:challenge fromDataSource:source]; +} + +- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source { + if (delegate_ != nil) + return [delegate_ webView:sender resource:identifier willSendRequest:request redirectResponse:redirectResponse fromDataSource:source]; + return nil; +} + +- (IMP) methodForSelector:(SEL)sel { + if (IMP method = [super methodForSelector:sel]) + return method; + fprintf(stderr, "methodForSelector:[%s] == NULL\n", sel_getName(sel)); + return NULL; +} + +- (BOOL) respondsToSelector:(SEL)sel { + if ([super respondsToSelector:sel]) + return YES; + // XXX: WebThreadCreateNSInvocation returns nil + //fprintf(stderr, "[%s]R?%s\n", class_getName(self->isa), sel_getName(sel)); + return delegate_ == nil ? NO : [delegate_ respondsToSelector:sel]; +} + +- (NSMethodSignature *) methodSignatureForSelector:(SEL)sel { + if (NSMethodSignature *method = [super methodSignatureForSelector:sel]) + return method; + //fprintf(stderr, "[%s]S?%s\n", class_getName(self->isa), sel_getName(sel)); + if (delegate_ != nil) + if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel]) + return sig; + // XXX: I fucking hate Apple so very very bad + return [NSMethodSignature signatureWithObjCTypes:"v@:"]; +} + +- (void) forwardInvocation:(NSInvocation *)inv { + SEL sel = [inv selector]; + if (delegate_ != nil && [delegate_ respondsToSelector:sel]) + [inv invokeWithTarget:delegate_]; +} + +@end +/* }}} */ + +@interface WebView (UICaboodle) +- (void) setScriptDebugDelegate:(id)delegate; +- (void) _setFormDelegate:(id)delegate; +- (void) _setUIKitDelegate:(id)delegate; +- (void) setWebMailDelegate:(id)delegate; +- (void) _setLayoutInterval:(float)interval; +@end + +@implementation WebScriptObject (UICaboodle) + +- (unsigned) count { + id length([self valueForKey:@"length"]); + if ([length respondsToSelector:@selector(intValue)]) + return [length intValue]; + else + return 0; +} + +- (id) objectAtIndex:(unsigned)index { + return [self webScriptValueAtIndex:index]; +} + +@end + +#define ShowInternals 0 +#define LogBrowser 0 + +#define lprintf(args...) fprintf(stderr, args) + +@implementation BrowserView + +#if ShowInternals +#include "UICaboodle/UCInternal.h" +#endif + ++ (void) _initialize { + [WebView enableWebThread]; + + WebPreferences *preferences([WebPreferences standardPreferences]); + [preferences setCacheModel:WebCacheModelDocumentBrowser]; + [preferences setOfflineWebApplicationCacheEnabled:YES]; + + [WebPreferences _setInitialDefaultTextEncodingToSystemEncoding]; + + $GSSystemCopyCapability = reinterpret_cast(dlsym(RTLD_DEFAULT, "GSSystemCopyCapability")); + $GSSystemGetCapability = reinterpret_cast(dlsym(RTLD_DEFAULT, "GSSystemGetCapability")); + $UIFormAssistant = objc_getClass("UIFormAssistant"); + + $UIWebBrowserView = objc_getClass("UIWebBrowserView"); + if ($UIWebBrowserView == nil) { + Wildcat_ = false; + $UIWebBrowserView = objc_getClass("UIWebDocumentView"); + } else { + Wildcat_ = true; + } +} + +- (void) dealloc { +#if LogBrowser + NSLog(@"[BrowserView dealloc]"); +#endif + + if (challenge_ != nil) + [challenge_ release]; + + WebThreadLock(); + + WebView *webview = [document_ webView]; + [webview setFrameLoadDelegate:nil]; + [webview setResourceLoadDelegate:nil]; + [webview setUIDelegate:nil]; + [webview setScriptDebugDelegate:nil]; + [webview setPolicyDelegate:nil]; + + /* XXX: these are set by UIWebDocumentView + [webview setDownloadDelegate:nil]; + [webview _setFormDelegate:nil]; + [webview _setUIKitDelegate:nil]; + [webview setEditingDelegate:nil];*/ + + /* XXX: no one sets this, ever + [webview setWebMailDelegate:nil];*/ + + [document_ setDelegate:nil]; + [document_ setGestureDelegate:nil]; + + if ([document_ respondsToSelector:@selector(setFormEditingDelegate:)]) + [document_ setFormEditingDelegate:nil]; + + [document_ setInteractionDelegate:nil]; + + [indirect_ setDelegate:nil]; + + //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + + [webview close]; + +#if RecycleWebViews + [document_ removeFromSuperview]; + [Documents_ addObject:[document_ autorelease]]; +#else + [document_ release]; +#endif + + [indirect_ release]; + + WebThreadUnlock(); + + [scroller_ setDelegate:nil]; + + if (button_ != nil) + [button_ release]; + if (style_ != nil) + [style_ release]; + if (function_ != nil) + [function_ release]; + if (finish_ != nil) + [finish_ release]; + if (closer_ != nil) + [closer_ release]; + if (special_ != nil) + [special_ release]; + + [scroller_ release]; + [indicator_ release]; + if (confirm_ != nil) + [confirm_ release]; + if (sensitive_ != nil) + [sensitive_ release]; + if (title_ != nil) + [title_ release]; + [super dealloc]; +} + +- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy { + [self loadRequest:[NSURLRequest + requestWithURL:url + cachePolicy:policy + timeoutInterval:120.0 + ]]; +} + +- (void) loadURL:(NSURL *)url { + [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy]; +} + +- (void) loadRequest:(NSURLRequest *)request { + pushed_ = true; + error_ = false; + + WebThreadLock(); + [document_ loadRequest:request]; + WebThreadUnlock(); +} + +- (void) reloadURL { + if (request_ == nil) + return; + + if ([request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil) + [self loadRequest:request_]; + else { + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:UCLocalize("RESUBMIT_FORM") + buttons:[NSArray arrayWithObjects:UCLocalize("CANCEL"), UCLocalize("SUBMIT"), nil] + defaultButtonIndex:0 + delegate:self + context:@"submit" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setNumberOfRows:1]; + [sheet popupAlertAnimated:YES]; + } +} + +- (WebView *) webView { + return [document_ webView]; +} + +- (UIWebDocumentView *) documentView { + return document_; +} + +/* XXX: WebThreadLock? */ +- (void) _fixScroller:(CGRect)bounds { + float extra; + + if (!editing_ || $UIFormAssistant == nil) + extra = 0; + else { + UIFormAssistant *assistant([$UIFormAssistant sharedFormAssistant]); + CGRect peripheral([assistant peripheralFrame]); +#if LogBrowser + NSLog(@"per:%f", peripheral.size.height); +#endif + extra = peripheral.size.height; + } + + CGRect subrect([scroller_ frame]); + subrect.size.height -= extra; + + if ([scroller_ respondsToSelector:@selector(setScrollerIndicatorSubrect:)]) + [scroller_ setScrollerIndicatorSubrect:subrect]; + + [document_ setValue:[NSValue valueWithSize:NSMakeSize(subrect.size.width, subrect.size.height)] forGestureAttribute:UIGestureAttributeVisibleSize]; + + CGSize size(size_); + size.height += extra; + [scroller_ setContentSize:size]; + + if ([scroller_ respondsToSelector:@selector(releaseRubberBandIfNecessary)]) + [scroller_ releaseRubberBandIfNecessary]; +} + +- (void) fixScroller { + CGRect bounds([document_ documentBounds]); +#if TrackResize + NSLog(@"_fs:(%f,%f+%f,%f)", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); +#endif + [self _fixScroller:bounds]; +} + +- (void) view:(UIView *)sender didSetFrame:(CGRect)frame { + size_ = frame.size; +#if TrackResize + NSLog(@"dsf:(%f,%f+%f,%f)", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); +#endif + [self _fixScroller:frame]; +} + +- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old { + [self view:sender didSetFrame:frame]; +} + +- (void) pushPage:(RVPage *)page { + [page setDelegate:delegate_]; + [self setBackButtonTitle:title_]; + [book_ pushPage:page]; +} + +- (void) _pushPage { + if (pushed_) + return; + // WTR: [self autorelease]; + pushed_ = true; + [book_ pushPage:self]; +} + +- (void) swapPage:(RVPage *)page { + [page setDelegate:delegate_]; + if (pushed_) + [book_ swapPage:page]; + else + [book_ pushPage:page]; +} + +- (BOOL) getSpecial:(NSURL *)url swap:(BOOL)swap { +#if LogBrowser + NSLog(@"getSpecial:%@", url); +#endif + + if (RVPage *page = [delegate_ pageForURL:url hasTag:NULL]) { + if (swap) + [self swapPage:page]; + else + [self pushPage:page]; + + return true; + } else + return false; +} + +- (void) formAssistant:(id)sender didBeginEditingFormNode:(id)node { +} + +- (void) formAssistant:(id)sender didEndEditingFormNode:(id)node { + [self fixScroller]; +} + +- (void) webViewShow:(WebView *)sender { + /* XXX: this is where I cry myself to sleep */ +} + +- (bool) _allowJavaScriptPanel { + return true; +} + +- (bool) allowSensitiveRequests { + return [self _allowJavaScriptPanel]; +} + +- (void) _promptForSensitive:(NSMutableArray *)array { + NSString *name([array objectAtIndex:0]); + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:nil + buttons:[NSArray arrayWithObjects:UCLocalize("YES"), UCLocalize("NO"), nil] + defaultButtonIndex:0 + delegate:indirect_ + context:@"sensitive" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + NSString *host(@"XXX"); + + [sheet setNumberOfRows:1]; + [sheet setBodyText:[NSString stringWithFormat:@"The website at %@ is requesting your phone's %@. This is almost certainly for product licensing purposes. Will you allow this?", host, name]]; + [sheet popupAlertAnimated:YES]; + + NSRunLoop *loop([NSRunLoop currentRunLoop]); + NSDate *future([NSDate distantFuture]); + + while (sensitive_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); + + NSNumber *sensitive([sensitive_ autorelease]); + sensitive_ = nil; + + [self autorelease]; + [array replaceObjectAtIndex:0 withObject:sensitive]; +} + +- (bool) promptForSensitive:(NSString *)name { + if (![self allowSensitiveRequests]) + return false; + + NSMutableArray *array([NSMutableArray arrayWithCapacity:1]); + [array addObject:name]; + + [self performSelectorOnMainThread:@selector(_promptForSensitive:) withObject:array waitUntilDone:YES]; + return [[array lastObject] boolValue]; +} + +- (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame { + if (![self _allowJavaScriptPanel]) + return; + [self retain]; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:nil + buttons:[NSArray arrayWithObjects:UCLocalize("OK"), nil] + defaultButtonIndex:0 + delegate:self + context:@"alert" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setBodyText:message]; + [sheet popupAlertAnimated:YES]; +} + +- (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame { + if (![self _allowJavaScriptPanel]) + return NO; + [self retain]; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:nil + buttons:[NSArray arrayWithObjects:UCLocalize("OK"), UCLocalize("CANCEL"), nil] + defaultButtonIndex:0 + delegate:indirect_ + context:@"confirm" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setNumberOfRows:1]; + [sheet setBodyText:message]; + [sheet popupAlertAnimated:YES]; + + NSRunLoop *loop([NSRunLoop currentRunLoop]); + NSDate *future([NSDate distantFuture]); + + while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); + + NSNumber *confirm([confirm_ autorelease]); + confirm_ = nil; + + [self autorelease]; + return [confirm boolValue]; +} + +- (void) setAutoPopup:(BOOL)popup { + popup_ = popup; +} + +- (void) setSpecial:(id)function { + if (special_ != nil) + [special_ autorelease]; + special_ = function == nil ? nil : [function retain]; +} + +- (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { + if (button_ != nil) + [button_ autorelease]; + button_ = button == nil ? nil : [[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:button]]] retain]; + + if (style_ != nil) + [style_ autorelease]; + style_ = style == nil ? nil : [style retain]; + + if (function_ != nil) + [function_ autorelease]; + function_ = function == nil ? nil : [function retain]; + + [self reloadButtons]; +} + +- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { + if (button_ != nil) + [button_ autorelease]; + button_ = button == nil ? nil : [button retain]; + + if (style_ != nil) + [style_ autorelease]; + style_ = style == nil ? nil : [style retain]; + + if (function_ != nil) + [function_ autorelease]; + function_ = function == nil ? nil : [function retain]; + + [self reloadButtons]; +} + +- (void) setFinishHook:(id)function { + if (finish_ != nil) + [finish_ autorelease]; + finish_ = function == nil ? nil : [function retain]; +} + +- (void) setPopupHook:(id)function { + if (closer_ != nil) + [closer_ autorelease]; + closer_ = function == nil ? nil : [function retain]; +} + +- (void) _openMailToURL:(NSURL *)url { + [UIApp openURL:url];// asPanel:YES]; +} + +- (void) webView:(WebView *)sender willBeginEditingFormElement:(id)element { + editing_ = true; +} + +- (void) webView:(WebView *)sender didBeginEditingFormElement:(id)element { + [self fixScroller]; +} + +- (void) webViewDidEndEditingFormElements:(WebView *)sender { + editing_ = false; + [self fixScroller]; +} + +- (void) webViewClose:(WebView *)sender { + [book_ close]; +} + +- (void) close { + [book_ close]; +} + +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { +} + +- (void) webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame { + NSLog(@"err:%@", error); +} + +- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id)listener { +#if LogBrowser + NSLog(@"nwa:%@", name); +#endif + + if (NSURL *url = [request URL]) { + if (name == nil) unknown: { + if (![self getSpecial:url swap:NO]) { + NSString *scheme([[url scheme] lowercaseString]); + if ([scheme isEqualToString:@"mailto"]) + [self _openMailToURL:url]; + else goto use; + } + } else if ([name isEqualToString:@"_open"]) + [delegate_ openURL:url]; + else if ([name isEqualToString:@"_popup"]) { + NSString *scheme([[url scheme] lowercaseString]); + if ([scheme isEqualToString:@"mailto"]) + [self _openMailToURL:url]; + else { + RVBook *book([[[RVPopUpBook alloc] initWithFrame:[delegate_ popUpBounds]] autorelease]); + [book setHook:indirect_]; + + RVPage *page([delegate_ pageForURL:url hasTag:NULL]); + if (page == nil) { + /* XXX: call createWebViewWithRequest instead? */ + + [self setBackButtonTitle:title_]; + + BrowserView *browser([[[class_ alloc] initWithBook:book] autorelease]); + [browser loadURL:url]; + page = browser; + } + + [book setDelegate:delegate_]; + [page setDelegate:delegate_]; + + [book setPage:page]; + [book_ pushBook:book]; + } + } else goto unknown; + + [listener ignore]; + } else use: + [listener use]; +} + +- (void) webView:(WebView *)sender decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { + if ([WebView canShowMIMEType:type]) + [listener use]; + else { + // XXX: handle more mime types! + [listener ignore]; + + WebView *webview([document_ webView]); + if (frame == [webview mainFrame]) + [UIApp openURL:[request URL]]; + } +} + +- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { + if (request == nil) ignore: { + [listener ignore]; + return; + } + + NSURL *url([request URL]); + NSString *host([url host]); + + if (url == nil) use: { + if (!error_ && [frame parentFrame] == nil) { + if (request_ != nil) + [request_ autorelease]; + request_ = [request retain]; +#if LogBrowser + NSLog(@"dpn:%@", request_); +#endif + } + + [listener use]; + + WebView *webview([document_ webView]); + if (frame == [webview mainFrame]) + [self _pushPage]; + return; + } +#if LogBrowser + else NSLog(@"nav:%@:%@", url, [action description]); +#endif + + const NSArray *capability; + + if ($GSSystemCopyCapability != NULL) { + capability = reinterpret_cast((*$GSSystemCopyCapability)(kGSDisplayIdentifiersCapability)); + capability = [capability autorelease]; + } else if ($GSSystemGetCapability != NULL) { + capability = reinterpret_cast((*$GSSystemGetCapability)(kGSDisplayIdentifiersCapability)); + } else + capability = nil; + + NSURL *open(nil); + + if (capability != nil && ( + [url isGoogleMapsURL] && [capability containsObject:@"com.apple.Maps"] && (open = [url mapsURL]) != nil|| + [host hasSuffix:@"youtube.com"] && [capability containsObject:@"com.apple.youtube"] && (open = [url youTubeURL]) != nil || + [url respondsToSelector:@selector(phobosURL)] && (open = [url phobosURL]) != nil + )) { + url = open; + open: + [UIApp openURL:url]; + goto ignore; + } + + int store(_not(int)); + if (NSURL *itms = [url itmsURL:&store]) { +#if LogBrowser + NSLog(@"itms#%@#%u#%@", url, store, itms); +#endif + + if (capability != nil && ( + store == 1 && [capability containsObject:@"com.apple.MobileStore"] || + store == 2 && [capability containsObject:@"com.apple.AppStore"] + )) { + url = itms; + goto open; + } + } + + NSString *scheme([[url scheme] lowercaseString]); + + if ([scheme isEqualToString:@"tel"]) { + // XXX: intelligence + goto open; + } + + if ([scheme isEqualToString:@"mailto"]) { + [self _openMailToURL:url]; + goto ignore; + } + + if ([self getSpecial:url swap:YES]) + goto ignore; + else if ([WebView _canHandleRequest:request]) + goto use; + else if ([url isSpringboardHandledURL]) + goto open; + else + goto use; +} + +- (void) webView:(WebView *)sender setStatusText:(NSString *)text { + //lprintf("Status:%s\n", [text UTF8String]); +} + +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + NSString *context([sheet context]); + + if ([context isEqualToString:@"alert"]) { + [self autorelease]; + [sheet dismiss]; + } else if ([context isEqualToString:@"confirm"]) { + switch (button) { + case 1: + confirm_ = [NSNumber numberWithBool:YES]; + break; + + case 2: + confirm_ = [NSNumber numberWithBool:NO]; + break; + } + + [sheet dismiss]; + } else if ([context isEqualToString:@"sensitive"]) { + switch (button) { + case 1: + sensitive_ = [NSNumber numberWithBool:YES]; + break; + + case 2: + sensitive_ = [NSNumber numberWithBool:NO]; + break; + } + + [sheet dismiss]; + } else if ([context isEqualToString:@"challenge"]) { + id sender([challenge_ sender]); + + switch (button) { + case 1: { + NSString *username([[sheet textFieldAtIndex:0] text]); + NSString *password([[sheet textFieldAtIndex:1] text]); + + NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]); + + [sender useCredential:credential forAuthenticationChallenge:challenge_]; + } break; + + case 2: + [sender cancelAuthenticationChallenge:challenge_]; + break; + + _nodefault + } + + [challenge_ release]; + challenge_ = nil; + + [sheet dismiss]; + } else if ([context isEqualToString:@"submit"]) { + switch (button) { + case 1: + break; + + case 2: + if (request_ != nil) { + WebThreadLock(); + [document_ loadRequest:request_]; + WebThreadUnlock(); + } + break; + + _nodefault + } + + [sheet dismiss]; + } +} + +- (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source { + challenge_ = [challenge retain]; + + NSURLProtectionSpace *space([challenge protectionSpace]); + NSString *realm([space realm]); + if (realm == nil) + realm = @""; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:realm + buttons:[NSArray arrayWithObjects:UCLocalize("LOGIN"), UCLocalize("CANCEL"), nil] + defaultButtonIndex:0 + delegate:self + context:@"challenge" + ] autorelease]; + + [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [sheet setNumberOfRows:1]; + + [sheet addTextFieldWithValue:@"" label:UCLocalize("USERNAME")]; + [sheet addTextFieldWithValue:@"" label:UCLocalize("PASSWORD")]; + + UITextField *username([sheet textFieldAtIndex:0]); { + UITextInputTraits *traits([username textInputTraits]); + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setKeyboardType:UIKeyboardTypeASCIICapable]; + [traits setReturnKeyType:UIReturnKeyNext]; + } + + UITextField *password([sheet textFieldAtIndex:1]); { + UITextInputTraits *traits([password textInputTraits]); + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setKeyboardType:UIKeyboardTypeASCIICapable]; + // XXX: UIReturnKeyDone + [traits setReturnKeyType:UIReturnKeyNext]; + [traits setSecureTextEntry:YES]; + } + + [sheet popupAlertAnimated:YES]; +} + +- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source { + return request; +} + +- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features { +//- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request userGesture:(BOOL)gesture { +#if LogBrowser + NSLog(@"cwv:%@ (%@): %@", request, title_, features == nil ? @"{}" : [features description]); + //NSLog(@"cwv:%@ (%@): %@", request, title_, gesture ? @"Yes" : @"No"); +#endif + + NSNumber *value([features objectForKey:@"width"]); + float width(value == nil ? 0 : [value floatValue]); + + RVBook *book(!popup_ ? book_ : [[[RVPopUpBook alloc] initWithFrame:[delegate_ popUpBounds]] autorelease]); + + /* XXX: deal with cydia:// pages */ + BrowserView *browser([[[class_ alloc] initWithBook:book forWidth:width] autorelease]); + + if (features != nil && popup_) { + [book setDelegate:delegate_]; + [book setHook:indirect_]; + [browser setDelegate:delegate_]; + + [browser loadRequest:request]; + + [book setPage:browser]; + [book_ pushBook:book]; + } else if (request == nil) { + [self setBackButtonTitle:title_]; + [browser setDelegate:delegate_]; + [browser retain]; + } else { + [self pushPage:browser]; + [browser loadRequest:request]; + } + + return [browser webView]; +} + +- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { + return [self webView:sender createWebViewWithRequest:request windowFeatures:nil]; + //return [self webView:sender createWebViewWithRequest:request userGesture:YES]; +} + +- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { + if ([frame parentFrame] != nil) + return; + + title_ = [title retain]; + [book_ reloadTitleForPage:self]; +} + +- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame { + /*if ([loading_ count] == 0) + [self retain];*/ + [loading_ addObject:[NSValue valueWithNonretainedObject:frame]]; + + if ([frame parentFrame] == nil) { + [document_ resignFirstResponder]; + + reloading_ = false; + + if (title_ != nil) { + [title_ release]; + title_ = nil; + } + + if (button_ != nil) { + [button_ release]; + button_ = nil; + } + + if (style_ != nil) { + [style_ release]; + style_ = nil; + } + + if (function_ != nil) { + [function_ release]; + function_ = nil; + } + + if (finish_ != nil) { + [finish_ release]; + finish_ = nil; + } + + if (closer_ != nil) { + [closer_ release]; + closer_ = nil; + } + + if (special_ != nil) { + [special_ release]; + special_ = nil; + } + + [book_ reloadTitleForPage:self]; + + if (Wildcat_) { + CGRect webrect = [scroller_ bounds]; + webrect.size.height = 1; + [document_ setFrame:webrect]; + } + + if ([scroller_ respondsToSelector:@selector(scrollPointVisibleAtTopLeft:)]) + [scroller_ scrollPointVisibleAtTopLeft:CGPointZero]; + else + [scroller_ scrollRectToVisible:CGRectZero animated:NO]; + + if ([scroller_ respondsToSelector:@selector(setZoomScale:duration:)]) + [scroller_ setZoomScale:1 duration:0]; + else if ([scroller_ respondsToSelector:@selector(_setZoomScale:duration:)]) + [scroller_ _setZoomScale:1 duration:0]; + /*else if ([scroller_ respondsToSelector:@selector(setZoomScale:animated:)]) + [scroller_ setZoomScale:1 animated:NO];*/ + + if (!Wildcat_) { + CGRect webrect = [scroller_ bounds]; + webrect.size.height = 0; + [document_ setFrame:webrect]; + } + } + + [self reloadButtons]; +} + +- (void) _finishLoading { + size_t count([loading_ count]); + /*if (count == 0) + [self autorelease];*/ + if (reloading_ || count != 0) + return; + if (finish_ != nil) + [self callFunction:finish_]; + [self reloadButtons]; +} + +- (bool) isLoading { + return [loading_ count] != 0; +} + +- (void) reloadButtons { + if ([self isLoading]) + [indicator_ startAnimation]; + else + [indicator_ stopAnimation]; + [super reloadButtons]; +} + +- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame { + return [document_ webView:sender shouldScrollToPoint:point forFrame:frame]; +} + +- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame { + return [document_ webView:sender didReceiveViewportArguments:arguments forFrame:frame]; +} + +- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame { + return [document_ webView:sender needsScrollNotifications:notifications forFrame:frame]; +} + +- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame { + [self _pushPage]; + return [document_ webView:sender didCommitLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame { + return [document_ webView:sender didReceiveDocTypeForFrame:frame]; +} + +- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { + [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]]; + [self _finishLoading]; + + if ([frame parentFrame] == nil) { + if (DOMDocument *document = [frame DOMDocument]) + if (DOMNodeList *bodies = [document getElementsByTagName:@"body"]) + for (DOMHTMLBodyElement *body in bodies) { + DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]); + + bool colored(false); + + if (DOMCSSPrimitiveValue *color = static_cast([style getPropertyCSSValue:@"background-color"])) { + if ([color primitiveType] == DOM_CSS_RGBCOLOR) { + DOMRGBColor *rgb([color getRGBColorValue]); + + float red([[rgb red] getFloatValue:DOM_CSS_NUMBER]); + float green([[rgb green] getFloatValue:DOM_CSS_NUMBER]); + float blue([[rgb blue] getFloatValue:DOM_CSS_NUMBER]); + float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]); + + UIColor *uic(nil); + + if (red == 0xc7 && green == 0xce && blue == 0xd5) + uic = [UIColor pinStripeColor]; + else if (alpha != 0) + uic = [UIColor + colorWithRed:(red / 255) + green:(green / 255) + blue:(blue / 255) + alpha:alpha + ]; + + if (uic != nil) { + colored = true; + [scroller_ setBackgroundColor:uic]; + } + } + } + + if (!colored) + [scroller_ setBackgroundColor:[UIColor pinStripeColor]]; + break; + } + } + + return [document_ webView:sender didFinishLoadForFrame:frame]; +} + +- (void) _didFailWithError:(NSError *)error forFrame:(WebFrame *)frame { + _trace(); + /*if ([frame parentFrame] == nil) + [self autorelease];*/ + + [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]]; + [self _finishLoading]; + + if (reloading_) + return; + + if ([frame parentFrame] == nil) { + [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", + [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString], + [[error localizedDescription] stringByAddingPercentEscapes] + ]]]; + + error_ = true; + } +} + +- (void) webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + [self _didFailWithError:error forFrame:frame]; + if ([document_ respondsToSelector:@selector(webView:didFailLoadWithError:forFrame:)]) + [document_ webView:sender didFailLoadWithError:error forFrame:frame]; +} + +- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + [self _didFailWithError:error forFrame:frame]; +} + +- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary { +#if LogBrowser || ForSaurik + lprintf("Console:%s\n", [[dictionary description] UTF8String]); +#endif +} + +- (void) webView:(WebView *)sender didReceiveMessage:(NSDictionary *)dictionary { +#if LogBrowser || ForSaurik + lprintf("Console:%s\n", [[dictionary description] UTF8String]); +#endif + if ([document_ respondsToSelector:@selector(webView:didReceiveMessage:)]) + [document_ webView:sender didReceiveMessage:dictionary]; +} + +- (void) webView:(id)sender willCloseFrame:(id)frame { + if ([document_ respondsToSelector:@selector(webView:willCloseFrame:)]) + [document_ webView:sender willCloseFrame:frame]; +} + +- (void) webView:(id)sender didFinishDocumentLoadForFrame:(id)frame { + if ([document_ respondsToSelector:@selector(webView:didFinishDocumentLoadForFrame:)]) + [document_ webView:sender didFinishDocumentLoadForFrame:frame]; +} + +- (void) webView:(id)sender didFirstLayoutInFrame:(id)frame { + if ([document_ respondsToSelector:@selector(webView:didFirstLayoutInFrame:)]) + [document_ webView:sender didFirstLayoutInFrame:frame]; +} + +- (void) webViewFormEditedStatusHasChanged:(id)changed { + if ([document_ respondsToSelector:@selector(webViewFormEditedStatusHasChanged:)]) + [document_ webViewFormEditedStatusHasChanged:changed]; +} + +- (void) webView:(id)sender formStateDidFocusNode:(id)formState { + if ([document_ respondsToSelector:@selector(webView:formStateDidFocusNode:)]) + [document_ webView:sender formStateDidFocusNode:formState]; +} + +- (void) webView:(id)sender formStateDidBlurNode:(id)formState { + if ([document_ respondsToSelector:@selector(webView:formStateDidBlurNode:)]) + [document_ webView:sender formStateDidBlurNode:formState]; +} + +/* XXX: fix this stupid include file +- (void) webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(WebSecurityOrigin *)origin database:(NSString *)database { + [origin setQuota:0x500000]; +}*/ + +- (void) webViewDidLayout:(id)sender { + [document_ webViewDidLayout:sender]; +} + +- (void) webView:(id)sender didFirstVisuallyNonEmptyLayoutInFrame:(id)frame { + [document_ webView:sender didFirstVisuallyNonEmptyLayoutInFrame:frame]; +} + +- (void) webView:(id)sender saveStateToHistoryItem:(id)item forFrame:(id)frame { + [document_ webView:sender saveStateToHistoryItem:item forFrame:frame]; +} + +- (void) webView:(id)sender restoreStateFromHistoryItem:(id)item forFrame:(id)frame force:(BOOL)force { + [document_ webView:sender restoreStateFromHistoryItem:item forFrame:frame force:force]; +} + +- (void) webView:(id)sender attachRootLayer:(id)layer { + [document_ webView:sender attachRootLayer:layer]; +} + +- (id) webView:(id)sender plugInViewWithArguments:(id)arguments fromPlugInPackage:(id)package { + return [document_ webView:sender plugInViewWithArguments:arguments fromPlugInPackage:package]; +} + +- (void) webView:(id)sender willShowFullScreenForPlugInView:(id)view { + [document_ webView:sender willShowFullScreenForPlugInView:view]; +} + +- (void) webView:(id)sender didHideFullScreenForPlugInView:(id)view { + [document_ webView:sender didHideFullScreenForPlugInView:view]; +} + +- (void) webView:(id)sender willAddPlugInView:(id)view { + [document_ webView:sender willAddPlugInView:view]; +} + +- (void) webView:(id)sender didObserveDeferredContentChange:(int)change forFrame:(id)frame { + [document_ webView:sender didObserveDeferredContentChange:change forFrame:frame]; +} + +- (void) webViewDidPreventDefaultForEvent:(id)sender { + [document_ webViewDidPreventDefaultForEvent:sender]; +} + +- (void) _setTileDrawingEnabled:(BOOL)enabled { + //[document_ setTileDrawingEnabled:enabled]; +} + +- (void) setViewportWidth:(float)width { + width_ = width != 0 ? width : [[self class] defaultWidth]; + [document_ setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10]; +} + +- (void) willStartGesturesInView:(UIView *)view forEvent:(GSEventRef)event { + [self _setTileDrawingEnabled:NO]; +} + +- (void) didFinishGesturesInView:(UIView *)view forEvent:(GSEventRef)event { + [self _setTileDrawingEnabled:YES]; + [document_ redrawScaledDocument]; +} + +- (void) scrollerWillStartDragging:(UIScroller *)scroller { + [self _setTileDrawingEnabled:NO]; +} + +- (void) scrollerDidEndDragging:(UIScroller *)scroller willSmoothScroll:(BOOL)smooth { + [self _setTileDrawingEnabled:YES]; +} + +- (void) scrollerDidEndDragging:(UIScroller *)scroller { + [self _setTileDrawingEnabled:YES]; +} + +- (id) initWithBook:(RVBook *)book forWidth:(float)width ofClass:(Class)_class { + if ((self = [super initWithBook:book]) != nil) { + class_ = _class; + loading_ = [[NSMutableSet alloc] initWithCapacity:3]; + popup_ = false; + + struct CGRect bounds = [self bounds]; + + scroller_ = [[objc_getClass(Wildcat_ ? "UIScrollView" : "UIScroller") alloc] initWithFrame:bounds]; + [self addSubview:scroller_]; + + [scroller_ setFixedBackgroundPattern:YES]; + [scroller_ setBackgroundColor:[UIColor pinStripeColor]]; + + [scroller_ setScrollingEnabled:YES]; + [scroller_ setClipsSubviews:YES]; + + if (!Wildcat_) + [scroller_ setAllowsRubberBanding:YES]; + + [scroller_ setDelegate:self]; + [scroller_ setBounces:YES]; + + if (!Wildcat_) { + [scroller_ setScrollHysteresis:8]; + [scroller_ setThumbDetectionEnabled:NO]; + [scroller_ setDirectionalScrolling:YES]; + //[scroller_ setScrollDecelerationFactor:0.99]; /* 0.989324 */ + [scroller_ setEventMode:YES]; + } + + if (Wildcat_) { + UIScrollView *scroller((UIScrollView *)scroller_); + //[scroller setDirectionalLockEnabled:NO]; + [scroller setDelaysContentTouches:NO]; + //[scroller setScrollsToTop:NO]; + //[scroller setCanCancelContentTouches:NO]; + } + + [scroller_ setShowBackgroundShadow:NO]; /* YES */ + //[scroller_ setAllowsRubberBanding:YES]; /* Vertical */ + + if (!Wildcat_) + [scroller_ setAdjustForContentSizeChange:YES]; /* NO */ + + CGRect webrect = [scroller_ bounds]; + webrect.size.height = 0; + + WebView *webview; + + WebThreadLock(); + +#if RecycleWebViews + document_ = [Documents_ lastObject]; + if (document_ != nil) { + document_ = [document_ retain]; + webview = [document_ webView]; + [Documents_ removeLastObject]; + [document_ setFrame:webrect]; + } else { +#else + if (true) { +#endif + document_ = [[$UIWebBrowserView alloc] initWithFrame:webrect]; + webview = [document_ webView]; + + // XXX: this is terribly (too?) expensive + //[document_ setDrawsBackground:NO]; + [webview setPreferencesIdentifier:@"Cydia"]; + + [document_ setTileSize:CGSizeMake(webrect.size.width, 500)]; + + if ([document_ respondsToSelector:@selector(enableReachability)]) + [document_ enableReachability]; + if ([document_ respondsToSelector:@selector(setAllowsMessaging:)]) + [document_ setAllowsMessaging:YES]; + if ([document_ respondsToSelector:@selector(useSelectionAssistantWithMode:)]) + [document_ useSelectionAssistantWithMode:0]; + + [document_ setTilingEnabled:YES]; + [document_ setDrawsGrid:NO]; + [document_ setLogsTilingChanges:NO]; + [document_ setTileMinificationFilter:kCAFilterNearest]; + + if ([document_ respondsToSelector:@selector(setDataDetectorTypes:)]) + /* XXX: abstractify */ + [document_ setDataDetectorTypes:0x80000000]; + else + [document_ setDetectsPhoneNumbers:NO]; + + [document_ setAutoresizes:YES]; + + [document_ setMinimumScale:0.25f forDocumentTypes:0x10]; + [document_ setMaximumScale:5.00f forDocumentTypes:0x10]; + [document_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10]; + //[document_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10]; + + [document_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2]; + + [document_ setMinimumScale:1.00f forDocumentTypes:0x8]; + [document_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8]; + [document_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8]; + + [document_ _setDocumentType:0x4]; + + if ([document_ respondsToSelector:@selector(setZoomsFocusedFormControl:)]) + [document_ setZoomsFocusedFormControl:YES]; + [document_ setContentsPosition:7]; + [document_ setEnabledGestures:0xa]; + [document_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled]; + [document_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller]; + + [document_ setSmoothsFonts:YES]; + [document_ setAllowsImageSheet:YES]; + [webview _setUsesLoaderCache:YES]; + + [webview setGroupName:@"CydiaGroup"]; + + WebPreferences *preferences([webview preferences]); + + if ([webview respondsToSelector:@selector(_setLayoutInterval:)]) + [webview _setLayoutInterval:0]; + else + [preferences _setLayoutInterval:0]; + } + + [self setViewportWidth:width]; + + [document_ setDelegate:self]; + [document_ setGestureDelegate:self]; + + if ([document_ respondsToSelector:@selector(setFormEditingDelegate:)]) + [document_ setFormEditingDelegate:self]; + + [document_ setInteractionDelegate:self]; + + [scroller_ addSubview:document_]; + + //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + + indirect_ = [[IndirectDelegate alloc] initWithDelegate:self]; + + [webview setFrameLoadDelegate:indirect_]; + [webview setPolicyDelegate:indirect_]; + [webview setResourceLoadDelegate:indirect_]; + [webview setUIDelegate:indirect_]; + + /* XXX: do not turn this on under penalty of extreme pain */ + [webview setScriptDebugDelegate:nil]; + + WebThreadUnlock(); + + CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite]; + indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(bounds.size.width - 39, 12, indsize.width, indsize.height)]; + [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite]; + + [self setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [scroller_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin]; + [document_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + /*UIWebView *test([[[UIWebView alloc] initWithFrame:[self bounds]] autorelease]); + [test loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.saurik.com/"]]]; + [self addSubview:test];*/ + } return self; +} + +- (id) initWithBook:(RVBook *)book forWidth:(float)width { + return [self initWithBook:book forWidth:width ofClass:[self class]]; +} + +- (id) initWithBook:(RVBook *)book { + return [self initWithBook:book forWidth:0]; +} + +- (NSString *) stringByEvaluatingJavaScriptFromString:(NSString *)script { + WebThreadLock(); + WebView *webview([document_ webView]); + NSString *string([webview stringByEvaluatingJavaScriptFromString:script]); + WebThreadUnlock(); + return string; +} + +- (void) callFunction:(WebScriptObject *)function { + WebThreadLock(); + + WebView *webview([document_ webView]); + WebFrame *frame([webview mainFrame]); + + id _private(MSHookIvar(webview, "_private")); + WebCore::Page *page(_private == nil ? NULL : MSHookIvar(_private, "page")); + WebCore::Settings *settings(page == NULL ? NULL : page->settings()); + + bool no; + if (settings == NULL) + no = 0; + else { + no = settings->JavaScriptCanOpenWindowsAutomatically(); + settings->setJavaScriptCanOpenWindowsAutomatically(true); + } + + if (UIWindow *window = [self window]) + if (UIResponder *responder = [window firstResponder]) + [responder resignFirstResponder]; + + JSObjectRef object([function JSObject]); + JSGlobalContextRef context([frame globalContext]); + JSObjectCallAsFunction(context, object, NULL, 0, NULL, NULL); + + if (settings != NULL) + settings->setJavaScriptCanOpenWindowsAutomatically(no); + + WebThreadUnlock(); +} + +- (void) didCloseBook:(RVBook *)book { + if (closer_ != nil) + [self callFunction:closer_]; +} + +- (void) __rightButtonClicked { + reloading_ = true; + [self reloadURL]; +} + +- (void) _rightButtonClicked { +#if !AlwaysReload + if (function_ != nil) + [self callFunction:function_]; + else +#endif + [self __rightButtonClicked]; +} + +- (id) _rightButtonTitle { + return UCLocalize("RELOAD"); +} + +- (id) rightButtonTitle { + return [self isLoading] ? @"" : button_ != nil ? button_ : [self _rightButtonTitle]; +} + +- (UINavigationButtonStyle) rightButtonStyle { + if (style_ == nil) normal: + return UINavigationButtonStyleNormal; + else if ([style_ isEqualToString:@"Normal"]) + return UINavigationButtonStyleNormal; + else if ([style_ isEqualToString:@"Back"]) + return UINavigationButtonStyleBack; + else if ([style_ isEqualToString:@"Highlighted"]) + return UINavigationButtonStyleHighlighted; + else if ([style_ isEqualToString:@"Destructive"]) + return UINavigationButtonStyleDestructive; + else goto normal; +} + +- (NSString *) title { + return title_ == nil ? UCLocalize("LOADING") : title_; +} + +- (NSString *) backButtonTitle { + return UCLocalize("BROWSER"); +} + +- (void) setPageActive:(BOOL)active { + if (!active) + [indicator_ removeFromSuperview]; + else + [[book_ navigationBar] addSubview:indicator_]; +} + +- (void) resetViewAnimated:(BOOL)animated { +} + +- (void) setPushed:(bool)pushed { + pushed_ = pushed; +} + ++ (float) defaultWidth { + return 980; +} + +@end diff --git a/UICaboodle/RVBook.h b/UICaboodle/RVBook.h new file mode 100644 index 0000000..376a153 --- /dev/null +++ b/UICaboodle/RVBook.h @@ -0,0 +1,81 @@ +#import + +#import + +@class NSMutableArray; +@class RVBook; +@class RVPage; +@class UINavigationBar; +@class UITransitionView; + +@interface UIView (PopUpView) +- (void) popFromSuperviewAnimated:(BOOL)animated; +- (void) popSubview:(UIView *)view; +@end + +@protocol RVNavigationBarDelegate +@end + +@protocol RVDelegate +- (void) setPageActive:(BOOL)active with:(id)object; +- (void) resetViewAnimated:(BOOL)animated with:(id)object; +- (void) reloadDataWith:(id)object; +- (void) popUpBook:(RVBook *)book; +- (CGRect) popUpBounds; +@end + +@protocol RVBookHook +- (void) didCloseBook:(RVBook *)book; +@end + +@interface RVBook : UIView < + RVNavigationBarDelegate +> { + NSMutableArray *pages_; + UINavigationBar *navbar_; + UITransitionView *transition_; + BOOL resetting_; + _transient id delegate_; + _transient id hook_; + UIToolbar *toolbar_; +} + +- (UINavigationBar *) navigationBar; + +- (id) initWithFrame:(CGRect)frame; +- (void) setDelegate:(id)delegate; +- (void) setHook:(id)hook; + +- (void) setPage:(RVPage *)page; + +- (void) swapPage:(RVPage *)page; + +- (void) pushPage:(RVPage *)page animated:(BOOL)animated; +- (void) pushPage:(RVPage *)page; + +- (void) popPages:(unsigned)pages; + +- (void) pushBook:(RVBook *)book; + +- (void) resetViewAnimated:(BOOL)animated; +- (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page; + +- (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page; +- (void) reloadTitleForPage:(RVPage *)page; +- (void) reloadButtonsForPage:(RVPage *)page; +- (NSString *) getTitleForPage:(RVPage *)page; + +- (void) reloadButtons; +- (void) reloadData; + +- (CGRect) pageBounds; +- (void) close; + +@end + +@interface RVPopUpBook : RVBook { + _transient RVBook *parent_; + bool cancel_; +} + +@end diff --git a/UICaboodle/RVBook.mm b/UICaboodle/RVBook.mm new file mode 100644 index 0000000..3fd525e --- /dev/null +++ b/UICaboodle/RVBook.mm @@ -0,0 +1,338 @@ +#import "RVBook.h" + +#import +#import + +#import + +#import +#import + +#import +#import + +#include +#include + +#include + +#import "RVPage.h" + +@interface NSObject (UICaboodleRVBook) +- (float) widthForButtonContents:(float)width; +@end + +@implementation NSObject (UICaboodleRVBook) + +- (float) widthForButtonContents:(float)width { + return width; +} + +@end + +@interface UIImage (UICaboodleRVBook) +- (float) widthForButtonContents:(float)width; +@end + +@implementation UIImage (UICaboodleRVBook) + +- (float) widthForButtonContents:(float)width { + return [self size].width + 8; +} + +@end + +@interface RVNavigationBar : UINavigationBar { +} + +- (id) createButtonWithContents:(id)contents width:(float)width barStyle:(int)barStyle buttonStyle:(int)style isRight:(BOOL)right; +@end + +@implementation RVNavigationBar + +- (id) createButtonWithContents:(id)contents width:(float)width barStyle:(int)barStyle buttonStyle:(int)style isRight:(BOOL)right { + float adjust = [contents widthForButtonContents:width]; + width = adjust; + return [super createButtonWithContents:contents width:width barStyle:barStyle buttonStyle:style isRight:right]; +} + +@end + +@implementation RVBook + +- (void) dealloc { + [navbar_ setDelegate:nil]; + if (toolbar_ != nil) + [toolbar_ setDelegate:nil]; + + for (RVPage *page in pages_) + [page setBook:nil]; + + [pages_ release]; + [navbar_ release]; + [transition_ release]; + if (toolbar_ != nil) + [toolbar_ release]; + [super dealloc]; +} + +- (UINavigationBar *) navigationBar { + return navbar_; +} + +- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button { + _assert([pages_ count] != 0); + RVPage *page = [pages_ lastObject]; + switch (button) { + case 0: [page _rightButtonClicked]; break; + case 1: [page _leftButtonClicked]; break; + } +} + +- (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item { + _assert([pages_ count] != 0); + if (!resetting_) + [[pages_ lastObject] setPageActive:NO]; + [pages_ removeLastObject]; + if (!resetting_) + [self resetViewAnimated:YES toPage:[pages_ lastObject]]; +} + +- (id) initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame]) != nil) { + pages_ = [[NSMutableArray arrayWithCapacity:4] retain]; + + struct CGRect bounds = [self bounds]; + CGSize navsize = [UINavigationBar defaultSize]; + CGRect navrect = {{0, 0}, {bounds.size.width, navsize.height}}; + + navbar_ = [[RVNavigationBar alloc] initWithFrame:navrect]; + [navbar_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [self addSubview:navbar_]; + + [navbar_ setBarStyle:0]; + [navbar_ setDelegate:self]; + + transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake( + bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height + )]; + + [transition_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; + + [self addSubview:transition_]; + } return self; +} + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + +- (void) setHook:(id)hook { + hook_ = hook; +} + +- (void) setPage:(RVPage *)page { + if ([pages_ count] != 0) + [[pages_ lastObject] setPageActive:NO]; + + [navbar_ disableAnimation]; + resetting_ = true; + for (unsigned i(0), pages([pages_ count]); i != pages; ++i) + [navbar_ popNavigationItem]; + resetting_ = false; + + [self pushPage:page]; + [navbar_ enableAnimation]; +} + +- (void) swapPage:(RVPage *)page { + if ([pages_ count] == 0) + return [self pushPage:page]; + + [[pages_ lastObject] setPageActive:NO]; + + [navbar_ disableAnimation]; + resetting_ = true; + [navbar_ popNavigationItem]; + resetting_ = false; + + [self pushPage:page animated:NO]; + [navbar_ enableAnimation]; +} + +- (void) pushPage:(RVPage *)page animated:(BOOL)animated { + NSString *title = [self getTitleForPage:page]; + + NSString *backButtonTitle = [page backButtonTitle]; + if (backButtonTitle == nil) + backButtonTitle = title; + + UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease]; + [navitem setBackButtonTitle:backButtonTitle]; + [navbar_ pushNavigationItem:navitem]; + + [page setFrame:[transition_ bounds]]; + [transition_ transition:(animated ? 1 : 0) toView:page]; + [page setPageActive:YES]; + + [pages_ addObject:page]; + [self reloadButtonsForPage:page]; + + if ([navbar_ respondsToSelector:@selector(setAccessoryView:animate:removeOnPop:)]) + [navbar_ setAccessoryView:[page accessoryView] animate:animated removeOnPop:NO]; + else + [navbar_ setAccessoryView:[page accessoryView] animate:animated]; +} + +- (void) pushPage:(RVPage *)page { + if ([pages_ count] != 0) + [[pages_ lastObject] setPageActive:NO]; + [self pushPage:page animated:([pages_ count] == 0 ? NO : YES)]; +} + +- (void) pushBook:(RVBook *)book { + [delegate_ popUpBook:book]; +} + +- (void) popPages:(unsigned)pages { + if (pages == 0) + return; + + [[pages_ lastObject] setPageActive:NO]; + + resetting_ = true; + for (unsigned i(0); i != pages; ++i) + [navbar_ popNavigationItem]; + resetting_ = false; + + [self resetViewAnimated:YES toPage:[pages_ lastObject]]; +} + +- (void) resetViewAnimated:(BOOL)animated { + resetting_ = true; + + if ([pages_ count] > 1) { + [navbar_ disableAnimation]; + while ([pages_ count] != (animated ? 2 : 1)) + [navbar_ popNavigationItem]; + [navbar_ enableAnimation]; + if (animated) + [navbar_ popNavigationItem]; + } + + resetting_ = false; + + [self resetViewAnimated:animated toPage:[pages_ lastObject]]; +} + +- (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page { + [page resetViewAnimated:animated]; + [page setFrame:[transition_ bounds]]; + [transition_ transition:(animated ? 2 : 0) toView:page]; + [page setPageActive:YES]; + [self reloadButtonsForPage:page]; + + if ([navbar_ respondsToSelector:@selector(setAccessoryView:animate:removeOnPop:)]) + [navbar_ setAccessoryView:[page accessoryView] animate:animated removeOnPop:NO]; + else + [navbar_ setAccessoryView:[page accessoryView] animate:animated]; +} + +- (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page { + if ([pages_ count] == 0 || page != [pages_ lastObject]) + return; + UINavigationItem *navitem = [navbar_ topItem]; + [navitem setBackButtonTitle:title]; +} + +- (void) reloadTitleForPage:(RVPage *)page { + if ([pages_ count] == 0 || page != [pages_ lastObject]) + return; + UINavigationItem *navitem = [navbar_ topItem]; + NSString *title = [self getTitleForPage:page]; + [navitem setTitle:title]; +} + +- (void) _leftButtonTitle:(NSString *&)leftButtonTitle style:(UINavigationButtonStyle &)leftButtonStyle forPage:(RVPage *)page { + leftButtonTitle = [page leftButtonTitle]; + leftButtonStyle = [page leftButtonStyle]; +} + +- (void) reloadButtonsForPage:(RVPage *)page { + if ([pages_ count] == 0 || page != [pages_ lastObject]) + return; + + NSString *leftButtonTitle; + UINavigationButtonStyle leftButtonStyle; + [self _leftButtonTitle:leftButtonTitle style:leftButtonStyle forPage:page]; + + UINavigationButtonStyle rightButtonStyle = [page rightButtonStyle]; + //[navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle] leftBack:(leftButtonTitle == nil)]; + + [navbar_ + showLeftButton:leftButtonTitle + withStyle:leftButtonStyle + rightButton:[page rightButtonTitle] + withStyle:rightButtonStyle + ]; +} + +- (NSString *) getTitleForPage:(RVPage *)page { + return [page title]; +} + +- (void) reloadData { + size_t count([pages_ count]); + for (size_t i(0); i != count; ++i) { + RVPage *page([pages_ objectAtIndex:(count - i - 1)]); + [page reloadData]; + } + + if (count != 0) { + RVPage *page([pages_ lastObject]); + [self reloadButtonsForPage:page]; + } +} + +- (void) reloadButtons { + size_t count([pages_ count]); + if (count != 0) { + RVPage *page([pages_ lastObject]); + [self reloadButtonsForPage:page]; + } +} + +- (CGRect) pageBounds { + return [transition_ bounds]; +} + +- (void) close { + if (hook_ != nil) + [hook_ didCloseBook:self]; +} + +@end + +@implementation RVPopUpBook + +- (void) _leftButtonTitle:(NSString *&)leftButtonTitle style:(UINavigationButtonStyle &)leftButtonStyle forPage:(RVPage *)page { + [super _leftButtonTitle:leftButtonTitle style:leftButtonStyle forPage:page]; + if ((cancel_ = leftButtonTitle == nil && [pages_ count] == 1)) { + leftButtonTitle = [[NSBundle mainBundle] localizedStringForKey:@"CANCEL" value:nil table:nil]; + leftButtonStyle = UINavigationButtonStyleNormal; + } +} + +- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button { + if (button == 1 && cancel_) + [self close]; + else + [super navigationBar:navbar buttonClicked:button]; +} + +- (void) close { + [self popFromSuperviewAnimated:YES]; + [super close]; +} + +@end diff --git a/UICaboodle/RVPage.h b/UICaboodle/RVPage.h new file mode 100644 index 0000000..284ebca --- /dev/null +++ b/UICaboodle/RVPage.h @@ -0,0 +1,42 @@ +#import + +#import + +@class NSString; +@class RVBook; + +@interface RVPage : UIView { + _transient RVBook *book_; + _transient id delegate_; +} + +- (bool) splitView; + +- (NSString *) title; +- (NSString *) backButtonTitle; +- (id) rightButtonTitle; +- (NSString *) leftButtonTitle; +- (UIView *) accessoryView; + +- (UIImage *) rightButtonImage; + +- (UINavigationButtonStyle) leftButtonStyle; +- (UINavigationButtonStyle) rightButtonStyle; + +- (void) _rightButtonClicked; +- (void) _leftButtonClicked; + +- (void) setPageActive:(BOOL)active; +- (void) resetViewAnimated:(BOOL)animated; + +- (void) setBackButtonTitle:(NSString *)title; + +- (void) reloadButtons; +- (void) reloadData; + +- (id) initWithBook:(RVBook *)book; + +- (void) setDelegate:(id)delegate; +- (void) setBook:(RVBook *)book; + +@end diff --git a/UICaboodle/RVPage.mm b/UICaboodle/RVPage.mm new file mode 100644 index 0000000..7154bab --- /dev/null +++ b/UICaboodle/RVPage.mm @@ -0,0 +1,87 @@ +#import "RVPage.h" + +#import +#import + +#import "RVBook.h" + +@implementation RVPage + +- (bool) splitView { + return false; +} + +- (NSString *) title { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (NSString *) backButtonTitle { + return nil; +} + +- (NSString *) leftButtonTitle { + return nil; +} + +- (id) rightButtonTitle { + return nil; +} + +- (UINavigationButtonStyle) leftButtonStyle { + return [self leftButtonTitle] == nil ? UINavigationButtonStyleBack : UINavigationButtonStyleNormal; +} + +- (UINavigationButtonStyle) rightButtonStyle { + return UINavigationButtonStyleNormal; +} + +- (void) _rightButtonClicked { + [self doesNotRecognizeSelector:_cmd]; +} + +- (void) _leftButtonClicked { + [self doesNotRecognizeSelector:_cmd]; +} + +- (UIView *) accessoryView { + return nil; +} + +- (UIImage *) rightButtonImage { + return nil; +} + +- (void) setPageActive:(BOOL)active { +} + +- (void) resetViewAnimated:(BOOL)animated { + [self doesNotRecognizeSelector:_cmd]; +} + +- (void) setBackButtonTitle:(NSString *)title { + [book_ setBackButtonTitle:title forPage:self]; +} + +- (void) reloadButtons { + [book_ reloadButtonsForPage:self]; +} + +- (void) reloadData { +} + +- (id) initWithBook:(RVBook *)book { + if ((self = [super initWithFrame:[book pageBounds]]) != nil) { + book_ = book; + } return self; +} + +- (void) setDelegate:(id)delegate { + delegate_ = delegate; +} + +- (void) setBook:(RVBook *)book { + book_ = book; +} + +@end diff --git a/UICaboodle/ResetView.h b/UICaboodle/ResetView.h new file mode 100644 index 0000000..1f92590 --- /dev/null +++ b/UICaboodle/ResetView.h @@ -0,0 +1,24 @@ +#import +#import + +#import + +@interface UIView (RVBook) +- (void) resetViewAnimated:(BOOL)animated; +- (void) clearView; +@end + +@interface UITable (RVBook) +- (void) resetViewAnimated:(BOOL)animated; +- (void) clearView; +@end + +@interface UITableView (RVBook) +- (void) resetViewAnimated:(BOOL)animated; +- (void) clearView; +@end + +@interface UISectionList (RVBook) +- (void) resetViewAnimated:(BOOL)animated; +- (void) clearView; +@end diff --git a/UICaboodle/ResetView.mm b/UICaboodle/ResetView.mm new file mode 100644 index 0000000..bd1c8ee --- /dev/null +++ b/UICaboodle/ResetView.mm @@ -0,0 +1,61 @@ +#import "ResetView.h" + +#include +#include + +#include + +#include +#include + +@implementation UIView (RVBook) + +- (void) resetViewAnimated:(BOOL)animated { + fprintf(stderr, "%s\n", class_getName(self->isa)); + _assert(false); +} + +- (void) clearView { + fprintf(stderr, "%s\n", class_getName(self->isa)); + _assert(false); +} + +@end + +@implementation UITable (RVBook) + +- (void) resetViewAnimated:(BOOL)animated { + [self selectRow:-1 byExtendingSelection:NO withFade:animated]; +} + +- (void) clearView { + [self clearAllData]; +} + +@end + +@implementation UITableView (RVBook) + +- (void) resetViewAnimated:(BOOL)animated { + //[self selectRowAtIndexPath:nil animated:animated scrollPosition:UITableViewScrollPositionNone]; + if (NSIndexPath *path = [self indexPathForSelectedRow]) + [self deselectRowAtIndexPath:path animated:animated]; +} + +- (void) clearView { + //XXX:[[self table] clearView]; +} + +@end + +@implementation UISectionList (RVBook) + +- (void) resetViewAnimated:(BOOL)animated { + [[self table] resetViewAnimated:animated]; +} + +- (void) clearView { + [[self table] clearView]; +} + +@end diff --git a/UICaboodle/UCInternal.h b/UICaboodle/UCInternal.h new file mode 100644 index 0000000..62b46e2 --- /dev/null +++ b/UICaboodle/UCInternal.h @@ -0,0 +1,10 @@ +- (NSMethodSignature *) methodSignatureForSelector:(SEL)selector { + fprintf(stderr, "[%s]S-%s\n", class_getName(self->isa), sel_getName(selector)); + return [super methodSignatureForSelector:selector]; +} + +- (BOOL) respondsToSelector:(SEL)selector { + BOOL responds = [super respondsToSelector:selector]; + fprintf(stderr, "[%s]R%c%s\n", class_getName(self->isa), (responds ? '+' : '-'), sel_getName(selector)); + return responds; +} diff --git a/UICaboodle/UCLocalize.h b/UICaboodle/UCLocalize.h new file mode 100644 index 0000000..ba66655 --- /dev/null +++ b/UICaboodle/UCLocalize.h @@ -0,0 +1,12 @@ +#ifndef UICABOODLE_UCLOCALIZE_H +#define UICABOODLE_UCLOCALIZE_H + +#import + +static inline NSString *UCLocalizeEx(NSString *key, NSString *value = nil) { + return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil]; +} + +#define UCLocalize(key) UCLocalizeEx(@ key) + +#endif/*UICABOODLE_UCLOCALIZE_H*/ diff --git a/UICaboodle/UCPlatform.h b/UICaboodle/UCPlatform.h new file mode 100644 index 0000000..4f2169e --- /dev/null +++ b/UICaboodle/UCPlatform.h @@ -0,0 +1,56 @@ +#define __STDC_LIMIT_MACROS +#include + +#include + +#include +#include + +#define _forever \ + for (;;) + +extern struct timeval _ltv; +extern bool _itv; + +#define _trace() do { \ + struct timeval _ctv; \ + gettimeofday(&_ctv, NULL); \ + if (!_itv) { \ + _itv = true; \ + _ltv = _ctv; \ + } \ + fprintf(stderr, "%lu.%.6u[%f]:_trace()@%s:%u[%s]\n", \ + _ctv.tv_sec, _ctv.tv_usec, \ + (_ctv.tv_sec - _ltv.tv_sec) + (_ctv.tv_usec - _ltv.tv_usec) / 1000000.0, \ + __FILE__, __LINE__, __FUNCTION__\ + ); \ + _ltv = _ctv; \ +} while (false) + +#define _assert(test) do \ + if (!(test)) { \ + fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \ + exit(-1); \ + } \ +while (false) + +#define _not(type) ((type) ~ (type) 0) + +#define _transient + +#define _label__(x) _label ## x +#define _label_(y) _label__(y) +#define _label _label_(__LINE__) + +#define _packed \ + __attribute__((__packed__)) +#define _finline \ + inline __attribute__((__always_inline__)) + +#define _assume(e) \ + _assert(e) + +#define _nodefault \ + default: \ + _assume(false); \ + throw; diff --git a/UICaboodle/UCString.h b/UICaboodle/UCString.h new file mode 100644 index 0000000..d776728 --- /dev/null +++ b/UICaboodle/UCString.h @@ -0,0 +1,42 @@ +#ifndef UICABOODLE_UCSTRING_H +#define UICABOODLE_UCSTRING_H + +#import + +@interface NSString (UIKit) +- (NSString *) stringByAddingPercentEscapes; +- (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1; +@end + +@interface NSString (UICaboodle) ++ (NSString *) stringWithDataSize:(double)size; +- (NSString *) stringByAddingPercentEscapesIncludingReserved; +@end + +@implementation NSString (UICaboodle) + ++ (NSString *) stringWithDataSize:(double)size { + unsigned power = 0; + while (size > 1024) { + size /= 1024; + ++power; + } + + static const char *powers_[] = {"B", "KiB", "MiB", "GiB"}; + + return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]]; +} + +- (NSString *) stringByAddingPercentEscapesIncludingReserved { + return [(id)CFURLCreateStringByAddingPercentEscapes( + kCFAllocatorDefault, + (CFStringRef) self, + NULL, + CFSTR(";/?:@&=+$,"), + kCFStringEncodingUTF8 + ) autorelease]; +} + +@end + +#endif/*UICABOODLE_UCSTRING_H*/ diff --git a/UICaboodle/UCYield.h b/UICaboodle/UCYield.h new file mode 100644 index 0000000..dc75f8f --- /dev/null +++ b/UICaboodle/UCYield.h @@ -0,0 +1,64 @@ +@interface NSObject (UICaboodle) +- (id) yieldToSelector:(SEL)selector withObject:(id)object; +- (id) yieldToSelector:(SEL)selector; +@end + +@implementation NSObject (UICaboodle) + +- (void) doNothing { +} + +- (void) _yieldToContext:(NSMutableArray *)context { _pooled + SEL selector(reinterpret_cast([[context objectAtIndex:0] pointerValue])); + id object([[context objectAtIndex:1] nonretainedObjectValue]); + volatile bool &stopped(*reinterpret_cast([[context objectAtIndex:2] pointerValue])); + + /* XXX: deal with exceptions */ + id value([self performSelector:selector withObject:object]); + + NSMethodSignature *signature([self methodSignatureForSelector:selector]); + [context removeAllObjects]; + if ([signature methodReturnLength] != 0 && value != nil) + [context addObject:value]; + + stopped = true; + + [self + performSelectorOnMainThread:@selector(doNothing) + withObject:nil + waitUntilDone:NO + ]; +} + +- (id) yieldToSelector:(SEL)selector withObject:(id)object { + /*return [self performSelector:selector withObject:object];*/ + + volatile bool stopped(false); + + NSMutableArray *context([NSMutableArray arrayWithObjects: + [NSValue valueWithPointer:selector], + [NSValue valueWithNonretainedObject:object], + [NSValue valueWithPointer:const_cast(&stopped)], + nil]); + + NSThread *thread([[[NSThread alloc] + initWithTarget:self + selector:@selector(_yieldToContext:) + object:context + ] autorelease]); + + [thread start]; + + NSRunLoop *loop([NSRunLoop currentRunLoop]); + NSDate *future([NSDate distantFuture]); + + while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); + + return [context count] == 0 ? nil : [context objectAtIndex:0]; +} + +- (id) yieldToSelector:(SEL)selector { + return [self yieldToSelector:selector withObject:nil]; +} + +@end diff --git a/Version.h b/Version.h new file mode 100644 index 0000000..7b7e57c --- /dev/null +++ b/Version.h @@ -0,0 +1 @@ +#define CYDIA_VERSION "1.1.31+17.g837b7c8" diff --git a/control b/control new file mode 100644 index 0000000..5843b4b --- /dev/null +++ b/control @@ -0,0 +1,15 @@ +Package: cydia +Priority: required +Section: Packaging +Maintainer: Jay Freeman (saurik) +Architecture: iphoneos-arm +Version: 1.0.3215-1 +Replaces: com.sosiphone.addcydia +Depends: apr-lib, apt7-lib, apt7-key, darwintools, pcre, sed, shell-cmds, system-cmds, uikittools (>= 1.0.3198) +Pre-Depends: dpkg (>= 1.14.25-8) +Conflicts: com.sosiphone.addcydia +Description: graphical iPhone front-end for APT +Name: Cydia Installer +Author: Jay Freeman (saurik) +Depiction: http://cydia.saurik.com/info/cydia/ +Tag: purpose::uikit, cydia::essential, role::enduser diff --git a/cydia.xcf b/cydia.xcf new file mode 100644 index 0000000..99874c9 Binary files /dev/null and b/cydia.xcf differ diff --git a/launch.xml b/launch.xml new file mode 100644 index 0000000..80d4806 --- /dev/null +++ b/launch.xml @@ -0,0 +1,9 @@ + + + + com.apple.springboard.launchapplications + + com.apple.springboard.opensensitiveurl + + + diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..0ed2d3e --- /dev/null +++ b/make.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/apl/tel/exec.sh :apr:apr-lib:apt7:apt7-lib:pcre make package diff --git a/makefile b/makefile new file mode 100644 index 0000000..e13ebfa --- /dev/null +++ b/makefile @@ -0,0 +1,42 @@ +ifndef PKG_TARG +target := +else +target := $(PKG_TARG)- +endif + +all: Cydia + +clean: + rm -f Cydia + +Cydia: Cydia.mm UICaboodle/*.mm ../mobilesubstrate/*.h #makefile + $(target)g++ -march=armv6 -mcpu=arm1176jzf-s -mthumb -I. -I../mobilesubstrate -fobjc-call-cxx-cdtors -g0 -O2 -Wall -Werror -o $@ $(filter %.mm,$^) -framework UIKit -framework IOKit -framework CoreFoundation -framework Foundation -framework CoreGraphics -framework GraphicsServices -framework QuartzCore -framework JavaScriptCore -framework WebCore -framework WebKit -lobjc -lapt-pkg -lpcre -fobjc-exceptions -F"$${PKG_ROOT}"/System/Library/PrivateFrameworks -multiply_defined suppress -lapr-1 + +sign: Cydia + CODESIGN_ALLOCATE=$$(which "$(target)codesign_allocate") ldid -Slaunch.xml Cydia + +package: sign + rm -rf _ + mkdir -p _/var/lib/cydia + + mkdir -p _/usr/libexec + svn export Library _/usr/libexec/cydia + cp -a /apl/tel/dest/iphoneos-arm/coreutils/usr/bin/du _/usr/libexec/cydia + + mkdir -p _/System/Library + svn export LaunchDaemons _/System/Library/LaunchDaemons + + mkdir -p _/Applications + svn export Cydia.app _/Applications/Cydia.app + cp -a Cydia _/Applications/Cydia.app/Cydia_ + chmod 6755 _/Applications/Cydia.app/Cydia_ + + mkdir -p _/System/Library/PreferenceBundles + svn export CydiaSettings.bundle _/System/Library/PreferenceBundles/CydiaSettings.bundle + + mkdir -p _/DEBIAN + echo "$$(cat control)"$$'\nInstalled-Size: '"$$(du -s _ | cut -f 1)" > _/DEBIAN/control + + dpkg-deb -Zlzma -b _ $(shell grep ^Package: control | cut -d ' ' -f 2-)_$(shell grep ^Version: control | cut -d ' ' -f 2)_iphoneos-arm.deb + +.PHONY: all clean sign