Compare commits

...

409 Commits

Author SHA1 Message Date
naturecodevoid
8cb5b3d47d ci: trigger build 2023-06-19 13:44:14 -07:00
Spidy123222
3d9c5ad890 Add onboarding issue number
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-06-14 19:02:15 -07:00
naturecodevoid
6f14b6b046 improve: move Reset Image Cache to Dev Mode 2023-06-14 18:54:18 -07:00
naturecodevoid
c3f5d9f218 fix(MDC): use free app limit for messages instead of hardcoding 3
it might be better to specify "with MDC"
2023-06-14 18:51:34 -07:00
naturecodevoid
91d3a528a0 improve: enable dev mode by default on simulator 2023-06-14 17:49:48 -07:00
naturecodevoid
0fc8f3d72e improve: fakeUndo3AppLimitPatch button wording 2023-06-14 17:48:39 -07:00
naturecodevoid
a959dd73bb feat(dev mode): add button to force 10 app limit 2023-06-14 17:47:30 -07:00
naturecodevoid
3c0995b5fa improve: lock more things behind UNSTABLE compile time flag 2023-06-13 20:40:35 -07:00
naturecodevoid
34bbe93b3d improve: move onboarding into a separate unstable feature 2023-06-13 20:37:26 -07:00
Spidy123222
ff24ea81c9 Add proper GitHub issues
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-06-12 23:27:27 -07:00
Spidy123222
18d251c364 Merge branch 'develop' into feature/unstable-features 2023-06-12 23:09:30 -07:00
Spidy123222
5864c283f6 Add EmuPalace Source 2023-06-12 20:35:36 -07:00
Spidy123222
be78fa4b91 Upload config (#383)
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-06-12 09:39:20 -07:00
naturecodevoid
2ff637f62e [skip ci] refactor: rename CowExploits to MDC 2023-06-04 08:08:48 -07:00
naturecodevoid
373a73c158 fix(MDC): make Info.plist valid again and actually use the info.plist preprocessor 2023-06-04 08:01:45 -07:00
naturecodevoid
95e98a17bb fix: add NSAppleMusicUsageDescription to Info.plist for MDC 2023-06-04 07:31:58 -07:00
naturecodevoid
8bd8ec8723 fix(MDC): revert f1shy's MDC changes and renames 2023-06-04 07:19:42 -07:00
naturecodevoid
e7f766095a fix(SwiftUI onboarding): make pairing file text wrap, only show full onboarding if SwiftUI unstable feature is enabled
also update anisette servers
2023-06-03 12:40:47 -07:00
naturecodevoid
7e9aafe86e ci: fix early build exit 2023-06-03 11:09:05 -07:00
naturecodevoid
51f900a5bb style: remove whitespace from README 2023-06-03 10:52:54 -07:00
naturecodevoid
02e63f2303 Merge remote-tracking branch 'origin/develop' into feature/unstable-features 2023-06-03 10:52:15 -07:00
naturecodevoid
b45108e519 ci: trigger build 2023-06-03 10:45:02 -07:00
naturecodevoid
28ecca5ed0 ci: make MDC ipa 2023-06-03 10:40:04 -07:00
naturecodevoid
742feed356 fix: compile error when not making an MDC build 2023-06-03 07:17:18 -07:00
naturecodevoid
b8c12a1041 fix: compile error 2023-06-02 22:06:05 -07:00
naturecodevoid
a6349198cf improve: use guard instead of if 2023-06-01 07:39:36 -07:00
naturecodevoid
465c87d442 feat: MDC (and update generated localizations and project file) 2023-06-01 07:38:26 -07:00
naturecodevoid
40c6d60138 improve: add more capabilities to FilledButtonStyle 2023-06-01 07:37:07 -07:00
naturecodevoid
7bb1c1cf05 refactor: Reduce duplicate code with Error.message()
also add some things I forgot in previous commits
2023-06-01 07:36:40 -07:00
naturecodevoid
175b5bec95 refactor: Reduce duplication code with UIApplication.keyWindow and .topController and improve alert function 2023-06-01 07:35:07 -07:00
naturecodevoid
f69ad9830a improve: put dev mode in better sections 2023-06-01 07:26:30 -07:00
naturecodevoid
3ee53e8c2b fix(SwiftUI): improve chevronRight colors for credit links 2023-05-29 20:33:30 -07:00
Spidy123222
b3abf69a02 Change label to 0.4.0
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-05-29 19:33:38 -07:00
Spidy123222
c530dc11ae Change default anisette to v3 anisette server (#367) 2023-05-29 19:15:15 -07:00
naturecodevoid
93ae81159e move UnstableFeaturesView to Unstable Features folder 2023-05-29 18:56:48 -07:00
naturecodevoid
6a942a3971 feat: enable unstable features on nightly and PR builds 2023-05-29 18:08:47 -07:00
naturecodevoid
5853aaa778 update comment with URL of example of onEnable and onDisable hooks to use a more permanent URL
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-05-27 22:21:36 -07:00
naturecodevoid
54703ddca3 feat: allow changing SideStore app icon from within My Apps 2023-05-27 22:10:26 -07:00
naturecodevoid
ce90ae4195 Remove Settings.bundle in favor of in-app advanced settings 2023-05-27 21:57:30 -07:00
naturecodevoid
026392dbc7 More improvements and fixes (see commit description)
- put SwiftUI in an unstable feature
- Add Reset adi.pb to SwiftUI settings
- Add localizations to more things such as Error Log and Refresh Attempts
- Move debug logging into Advanced Settings
- Add padding to version text at the bottom of SwiftUI settings
- Add some things to Unstable Features such as nesting the Feature enum in UnstableFeatures and allowing on enable/disable hooks
- Don't use ObservableObject for UnstableFeatures as it's not needed
- fix a bug with unstable features where the toggle would be reverted if you go into another tab and then back
- Use SwiftUI advanced settings in UIKit
2023-05-27 21:53:04 -07:00
naturecodevoid
d2c15b5acd project: strip all symbols in an attempt to exclude swiftui from non-unstable builds and reduce binary size 2023-05-24 21:02:23 -07:00
naturecodevoid
2219035cd0 More improvements to unstable features and advanced settings
- added description of what they are and notice if there are none available
- move them to advanced settings
- add alert for unstable features in dev mode if the build does not have them enabled
- move stuff out of the danger zone and into anisette section in advanced settings
2023-05-24 21:01:11 -07:00
Joelle Stickney
d368ddbd11 Merge pull request #365 from lonkelle/develop
Co-authored-by: Joe Mattiello <mail@joemattiello.com>
2023-05-24 23:11:22 -04:00
Joelle Stickney
e5c6521a15 Co-authoring all the things
Co-authored-by: Joe Mattiello <mail@joemattiello.com>
2023-05-24 23:08:33 -04:00
Joelle Stickney
898a59768e Update README.md
Co-authored-by: Joe Mattiello <mail@joemattiello.com>
2023-05-24 23:04:45 -04:00
naturecodevoid
a8917f095e fix: remove jkcoxson anisette servers from SwiftUI advanced settings 2023-05-24 07:39:58 -07:00
naturecodevoid
3cab2e5d15 fix: add https to ani.sidestore.io in SwiftUI advanced settings 2023-05-24 07:38:39 -07:00
Joelle Stickney
a85bc93142 Update README.md
Co-authored-by: Joe Mattiello <mail@joemattiello.com>

Signed-off-by: Joelle Stickney <joellestickney+commit@gmail.com>
2023-05-24 03:56:58 -04:00
Joelle Stickney
c6c1f9faa0 Update README.md
Co-authored-by: JoeMatt <mail@joemattiello.com>

Signed-off-by: Joelle Stickney <joellestickney+commit@gmail.com>
2023-05-24 03:55:28 -04:00
Joelle Stickney
0eea19c9cc Update README.md
Signed-off-by: Joelle Stickney <joellestickney+commit@gmail.com>
2023-05-24 03:53:51 -04:00
naturecodevoid
e2c5267d3f fix: rename allowDevModeOnlyFeatures to inDevMode and simplify dummy filtering 2023-05-20 14:35:59 -07:00
naturecodevoid
5709229fdf fix: remove weird character
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-05-20 14:28:33 -07:00
naturecodevoid
e1607d2f61 fix: Hide ratings in app detail 2023-05-20 14:23:45 -07:00
naturecodevoid
637a0354c5 feat: view to enable/disable Unstable Features 2023-05-20 14:23:25 -07:00
Fabian Thies
9c3461b0c6 Fix disabling horizontal scroll on onboarding screens and made showing only certain steps more reusable 2023-05-20 22:14:50 +02:00
naturecodevoid
e3103b3034 Move TabBarController.swift into UIKit folder 2023-05-20 12:42:19 -07:00
naturecodevoid
2db073d2c5 Reorganize AltStore project into UIKit and SwiftUI folders 2023-05-20 12:35:53 -07:00
naturecodevoid
e06cca8224 Fixes (see commit description)
- Fix issue caused by merge
- Improve icons in onboarding
- Use onboarding's pairing file step properly
2023-05-20 12:25:07 -07:00
naturecodevoid
3a7cd29b22 Merge SwiftUI (#221) + SwiftUI improvements (#265)
commit 22f1ff7cd7d4d4750eeda2067d23846900239b83
Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
Date:   Sat May 20 11:29:01 2023 -0700

    fix: actually disable LocalConsole's character limit

commit 4b51915da7bc0637ccf819ac45c7d727d450ae12
Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
Date:   Sat May 20 11:27:12 2023 -0700

    Merge SwiftUI improvements (#265)

    commit 7f73f2adef
    Merge: 72f34dd2 38a1c7ee
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sat May 20 11:23:07 2023 -0700

        Merge remote-tracking branch 'origin/fabianthdev/feature/SwiftUI' into naturecodevoid/swiftui-improvements

    commit 72f34dd286
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Apr 12 18:21:49 2023 -0700

        feat: default to Storm icon for PR builds

        Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

    commit 060c37c423
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 19:40:53 2023 -0700

        fix(icons): sky appears correctly in light mode

    commit 8c2968aeb3
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 14:29:03 2023 -0700

        fix: build errors

    commit 4f512b6318
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:54:01 2023 -0700

        project(minimuxer): fix actions build error

    commit 5b752cf26e
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:51:54 2023 -0700

        fix: remove duplicate isSideStore checks with a StoreApp extension

    commit 62a478277e
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:41:58 2023 -0700

        fix(AsyncFallibleButton): try to use failureReason and then fallback to localizedDescription

    commit 994b2318a9
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:38:44 2023 -0700

        feat(dev mode): add AFC file explorer and dump profiles

    commit 423ac28ba3
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:35:14 2023 -0700

        project(AltStore): xcode wants to move these around I guess

    commit af2cdd48d6
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:34:57 2023 -0700

        feat: add debug logging toggle

    commit 44fe0c5686
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:33:11 2023 -0700

        project(minimuxer): Add libminimuxer as an input file for build step

    commit 3d46a3069a
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:32:22 2023 -0700

        fix: handle source conflict in merge policy

    commit 82e8fb7389
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Apr 9 13:31:39 2023 -0700

        docs: include info on Developer Mode

    commit 1dd0cd7d90
    Merge: 92a9650c 566841a9
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Apr 6 21:07:33 2023 -0700

        Merge branch 'fabianthdev/feature/SwiftUI' into naturecodevoid/swiftui-improvements

    commit 566841a9a6
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Apr 6 21:06:07 2023 -0700

        Fix not being able to open the project

    commit 92a9650c0c
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Apr 6 20:49:49 2023 -0700

        Apply DevModeView suggestion

    commit df94e79472
    Merge: d3cfc4ba cd2c5ad7
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Apr 6 20:48:52 2023 -0700

        Merge branch 'fabianthdev/feature/SwiftUI' into naturecodevoid/swiftui-improvements

    commit cd2c5ad7b4
    Merge: 3466870d 6146f1bd
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Apr 6 20:43:10 2023 -0700

        Merge remote-tracking branch 'origin/develop' into fabianthdev/feature/SwiftUI

    commit d3cfc4bab9
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 22 13:05:11 2023 -0800

        FileExplorer: Replace file when inserting

    commit df62461d4a
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 22 13:04:52 2023 -0800

        Settings: Add Export Logs and commit xcodeproj changes

    commit 817d2de5e0
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 22 12:19:07 2023 -0800

        Rename View+SideStore

    commit 3ea478ad05
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 22 12:18:42 2023 -0800

        DevMode: Add password

    commit 13f9a9d1bf
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 22 11:43:13 2023 -0800

        AdvancedSettingsView: improve anisette URL by using a label instead of a placeholder

    commit 3821a6034d
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Tue Feb 21 17:34:56 2023 -0800

        project: attempt to fix crashing on launch

    commit 3e8d7da0c3
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 13:49:22 2023 -0800

        AdvancedSettingsView: Remove autocomplete from anisette URL text field

    commit a42c1a705f
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 13:25:59 2023 -0800

        SettingsView: Adjust ordering a little bit and remove accent color

    commit 30efc6f210
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 13:19:26 2023 -0800

        LaunchViewController: Revert changes

    commit 60412721ee
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 13:04:42 2023 -0800

        Fix build errors

        hopefully this doesn't have any unintended side effects

    commit cba00a3b9d
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 12:03:22 2023 -0800

        Add Advanced Settings in-app

    commit 2aa880d10e
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 10:56:01 2023 -0800

        Fix build errors after merge

    commit 47848ddd18
    Merge: deac960e 3466870d
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 09:56:21 2023 -0800

        Merge branch 'fabianthdev/feature/SwiftUI' into naturecodevoid/swiftui-improvements

        Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

    commit deac960e10
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 09:54:56 2023 -0800

        Revert OutputCapturer changes since Fabian already added the fix

    commit 9f05123e42
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 09:16:49 2023 -0800

        AppIconView: Make isSideStore required

    commit d9a4b07095
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 09:16:07 2023 -0800

        Fix changing SideStore app icon not displaying My Apps

    commit 839699ee03
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 09:00:19 2023 -0800

        Icons: add Vista by Swifticul

    commit 81409227d6
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 08:06:33 2023 -0800

        Add developer mode

    commit 49b9be160f
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sun Feb 19 07:57:29 2023 -0800

        AppRowView: Disable ratings if there aren't any ratings

    commit 3466870d8f
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sun Feb 19 14:31:01 2023 +0100

        [ADD] UI for writing an app review and submit an app rating

    commit ffe8a92a4e
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sun Feb 19 14:30:21 2023 +0100

        [CHANGE] UI fixes and SwiftUI previews for easier development

    commit bc2cae46a8
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sun Feb 19 14:25:13 2023 +0100

        [ADD] Refresh all apps functionality in MyAppsView

    commit a95d8a502c
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sun Feb 19 11:40:26 2023 +0100

        [FIX] STDOUT output not visible in Xcode console

    commit 19e66112dd
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sat Feb 18 20:27:06 2023 -0800

        SourcesView: Fix 1 trusted source causing an error making all trusted sources fail to load

    commit 0d3cb843ea
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sat Feb 18 20:26:32 2023 -0800

        SourcesViewController: Fix 1 trusted source causing an error making all trusted sources fail to load

    commit df1a662acc
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sat Feb 18 20:25:58 2023 -0800

        FetchTrustedSourcesOperation: Remove redundant if statement

    commit 684c9e08eb
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Sat Feb 18 10:48:05 2023 -0800

        Fix HMR

    commit c585c57965
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Fri Feb 17 18:51:06 2023 -0800

        Revert fixes since it didn't actually fix the problem

    commit 3605ca6422
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Fri Feb 17 18:20:56 2023 -0800

        Fix HMR again

    commit 40f4c94f4d
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Fri Feb 17 18:11:25 2023 -0800

        Fix HMR crashing the app

    commit 986465d8f4
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Fri Feb 17 17:44:56 2023 -0800

        Project: Add HMR

        https://github.com/krzysztofzablocki/Inject#individual-developer-setup-once-per-machine

    commit 09db1ba9fc
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Feb 16 18:13:32 2023 -0800

        SettingsView: Move App Icon to a new, general settings section

    commit 8874480b8c
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Feb 16 17:57:51 2023 -0800

        Icons: invert Sky

    commit f0cc4613da
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Thu Feb 16 17:57:19 2023 -0800

        AppIconsView: Add artists

    commit bec78322a4
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 15 21:00:28 2023 -0800

        actions: Add build step that changes default icon

    commit 03777fd2e7
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 15 20:49:07 2023 -0800

        Icons: add Sky, Honeydew, Midnight

    commit 96ae60a9f2
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 15 19:36:10 2023 -0800

        AppIconsView: improve the way primary icons are handled

    commit c7ad6b10a1
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 15 19:35:57 2023 -0800

        Icons: reduce image sizes

    commit 8b8e471c97
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Wed Feb 15 18:52:42 2023 -0800

        Add App Icon changer

    commit 38c0a8a9a3
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Tue Feb 14 08:24:49 2023 -0800

        Fix ConnectAppleIDView being shoved into a sidebar on iPad

    commit e7ff6496c1
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Tue Feb 14 08:20:16 2023 -0800

        AuthenticationOperation: fix 2FA code not being displayed

        Bandaid fix, it would be better to have the alert in ConnectAppleIDView

    commit c2e89b09ea
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Mon Feb 13 21:44:48 2023 -0800

        RootView: Fix UI being shoved into sidebar on iPad (closes #264, thanks @Swifticul!)

    commit ec4dbb6679
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Mon Feb 13 21:06:59 2023 -0800

        OutputCapturer: fix logging disappearing from Xcode/idevicedebug run

    commit d80c9ba2a8
    Author: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
    Date:   Mon Feb 13 21:06:17 2023 -0800

        remove unused apps.json files

    commit b2f81bf7c6
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Feb 13 18:56:34 2023 +0100

        [ADD] LocalConsole showing STDOUT and STDERR

    commit 2fffa6e122
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sat Feb 4 14:35:58 2023 +0100

        [FIX] App compatibility info

    commit 723c8e9539
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sat Feb 4 14:29:02 2023 +0100

        [ADD] Debug entries for refresh attempts, sending feedback, advanced settings, and resetting the pairing file

    commit 07159b0ea6
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sat Feb 4 13:07:04 2023 +0100

        [ADD] Error log view

    commit e0bd54389c
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sat Feb 4 12:55:25 2023 +0100

        [FIX] Various UI issues

    commit 57213fbf0c
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sat Feb 4 12:46:43 2023 +0100

        [ADD] App report button and trusted source badge in app detail view

    commit 0239dfcd6d
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Feb 3 18:19:07 2023 +0100

        [FIX] AppIDsView and authentication workflow

    commit 5af6f825ee
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Feb 3 18:16:48 2023 +0100

        [FIX] Full screen app screenshot previews

    commit b4859512ab
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Feb 3 14:58:06 2023 +0100

        [FIX] Accent color

    commit 3d0f385af7
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Tue Jan 31 22:38:42 2023 +0100

        [CHANGE] Overhaul of the AppDetailView with version history, reviews & ratings, and app information

    commit f3e58e1485
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Tue Jan 31 22:37:37 2023 +0100

        [UPDATE] AppPillButton dimensions and expiration text

    commit d3e04c1db7
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Tue Jan 31 22:35:09 2023 +0100

        [FIX] Show App IDs button only if user is logged in with their Apple ID

    commit ed1970245a
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Tue Jan 31 22:32:11 2023 +0100

        [ADD] Load and show trusted sources with option to add them to the app

    commit 15dd885a1b
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Tue Jan 31 22:30:21 2023 +0100

        [ADD] Credits section in SettingsView

    commit 4663c01700
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Jan 16 21:23:16 2023 +0100

        [CHANGE] Extracted all strings into the Localizable.strings

    commit e733601c66
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Jan 16 19:03:33 2023 +0100

        [FIX] Text alignment in SettingsView

    commit fc974a8079
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Jan 16 19:02:58 2023 +0100

        [ADD] Hint for new users who don't have any sources

    commit 6aaadc79e5
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Jan 16 18:59:39 2023 +0100

        [ADD] AppScreenshot view with ImageProcessor to automatically rotate landscape screenshots

    commit b9177e89c6
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 13:37:38 2023 +0100

        [FIX] Issues introduced by changes to the AltSource specification.

    commit 1531c0a77f
    Author: Fabian Thies <github@fabian-thies.de>
    Date:   Fri Jan 13 12:48:27 2023 +0100

        [UPDATE] Translations (#7)

        This PR merges all the new translations made on the SideStore weblate instance (https://translate.sidestore.io/projects/sidestore/app).

        New translations:
        - French
        - Korean

        Updated translations:
        - Spanish

        Co-authored-by: bogotesr <bogotesr@gmail.com>
        Co-authored-by: GABO1423 <35014183+GABO1423@users.noreply.github.com>
        Co-authored-by: Joss Laymon <71040782+bogotesr@users.noreply.github.com>
        Co-authored-by: mindfreakdev <shost212@gmail.com>
        Co-authored-by: Python <rjp2030@proton.me>
        Co-authored-by: Testi Cules <ervd516@gmail.com>

    commit 1dde36face
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 12:25:50 2023 +0100

        [FIX] Changes made by Xcode 14 after building the app

    commit c3c3783ba4
    Author: Upal <shost212@gmail.com>
    Date:   Mon Dec 26 19:18:33 2022 +0530

        Added Hindi Language (#5)

        * Added Hindi Language

    commit 8400af3423
    Author: mindfreakdev <shost212@gmail.com>
    Date:   Sun Dec 25 16:52:01 2022 +0530

        Added Dutch Language

    commit 243c7efc09
    Author: mindfreakdev <shost212@gmail.com>
    Date:   Sun Dec 25 12:30:42 2022 +0530

        Added Ukrainian Language

    commit 0298a0235b
    Author: mindfreakdev <shost212@gmail.com>
    Date:   Sun Dec 25 12:28:00 2022 +0530

        Added Ukrainian Language

    commit e5b2496b09
    Author: Gabriel Morazán <35014183+GABO1423@users.noreply.github.com>
    Date:   Sun Dec 25 01:08:47 2022 -0400

        Screen Crunch sucks

        Signed-off-by: Gabriel Morazán <35014183+GABO1423@users.noreply.github.com>

    commit 75c52a3af2
    Author: GABO1423 <35014183+GABO1423@users.noreply.github.com>
    Date:   Sun Dec 25 00:58:22 2022 -0400

        Spanish Translation Tweaks

    commit 2c07009b04
    Author: bogotesr <bogotesr@gmail.com>
    Date:   Sat Dec 24 21:06:28 2022 -0700

        Add es-419 and finish adding support for the translations

        Added Latin American Spanish (probably not the best translation)

        Made everything reference the swiftgen stuff rather than having strings

    commit 6257fdcd61
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Thu Dec 22 10:29:57 2022 +0100

        [CHANGE] Extracted some example strings and replaced them by generated localized strings

    commit e23956d4ed
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Thu Dec 22 10:21:57 2022 +0100

        [ADD] SwiftGen configuration and generated files

    commit 1341de8315
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Thu Dec 22 10:10:58 2022 +0100

        [ADD] Empty Localizable.strings

    commit 77f5844e4d
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 12:04:10 2023 +0100

        [WIP] AppScreenshot view with ImageProcessor to automatically rotate landscape images. Possible through my fork of the AsyncImage framework.

    commit b3c4819e8d
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 12:02:56 2023 +0100

        [WIP] Fetch trusted sources in SourcesView

    commit a6ca73f8fc
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 12:02:06 2023 +0100

        [WIP] AppIDs view in My Apps section

    commit f17d00c0bc
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 12:00:00 2023 +0100

        [ADD] Badge in AppDetailView for apps from the official source and (WIP) trusted sources

    commit 875453533b
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 11:58:25 2023 +0100

        [ADD] Hint view in MyAppsView telling the user about where to find updates in the future if no updates are available

    commit 9a7a39a58e
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 11:54:44 2023 +0100

        [FIX] App permission icon color

    commit 65db392388
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Jan 13 11:51:06 2023 +0100

        [ADD] Show source name and external url domain in NewsItemView

    commit 6a6fc22995
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Dec 23 16:02:57 2022 +0100

        [ADD] Full-screen app screenshot preview

    commit 5697c4c063
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Dec 23 15:21:16 2022 +0100

        [CHANGE] Replace system image name strings with SFSymbols

    commit bcd54067d3
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Fri Dec 23 13:12:39 2022 +0100

        [ADD] Dependency: SFSafeSymbols

    commit c7ce32a562
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Wed Dec 21 17:49:49 2022 +0100

        [ADD] WIP: Promoted category cards and app list filter button in BrowseView

    commit 5a1496a3cd
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Wed Dec 21 17:48:45 2022 +0100

        [FIX] AccentColor in dark mode

    commit 497c048240
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Wed Dec 21 17:48:23 2022 +0100

        [ADD] Carousel for SideStore-specific announcements in NewsView

    commit 02e48a207f
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Wed Dec 21 17:45:44 2022 +0100

        [ADD] WIP: Add My Apps view with support for sideloading new apps, refreshing installed apps and much more

    commit a0eb30f98e
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Dec 12 19:20:54 2022 +0100

        [CHANGE] Fixed the AppRowView background blur effect

    commit 378631e976
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Dec 12 19:20:10 2022 +0100

        [ADD] Backported dismiss() environment variable to let views dismiss themselves

    commit 0e7083539d
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Dec 12 19:18:57 2022 +0100

        [ADD] Search bar for BrowseView on iOS 15

    commit 0c034b61d9
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Dec 12 19:16:36 2022 +0100

        [CHANGE] Fetch news when NewsView appears

    commit 89dea75b84
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Dec 12 19:15:16 2022 +0100

        Improved app detail view

    commit 81ea791b63
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Mon Dec 12 19:12:38 2022 +0100

        [ADD] Authentication view for connecting SideStore to an Apple ID

    commit c81f716427
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sun Nov 27 16:41:30 2022 +0100

        [WIP] Fixed the app permissions grid in AppDetailView

    commit eb151d74dd
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sun Nov 27 16:17:08 2022 +0100

        [ADD] Expandable app and version description texts

    commit 0dc7af5e51
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Sun Nov 27 00:26:15 2022 +0100

        [ADD] iOS 13 compatible AsyncImage implementation with cache

    commit d3e8473f45
    Author: Fabian Thies <git@fabian-thies.de>
    Date:   Wed Nov 23 22:34:02 2022 +0100

        [ADD] News, Browse and Settings views ported to SwiftUI

        This commit contains WIP SwiftUI versions of most of the views in SideStore.

commit 38a1c7eef6
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sat May 20 20:05:36 2023 +0200

    Fix rebase issues

commit f6252c3a8b
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sat May 20 19:10:51 2023 +0200

    Fix trusted sources being enabled in onboarding process regardless of user choice

commit 653d80b88e
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri May 19 13:14:15 2023 +0200

    Add onboarding screens for an easy setup of SideStore

commit 89609ad35c
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sun Feb 19 14:31:01 2023 +0100

    [ADD] UI for writing an app review and submit an app rating

commit 2211013e57
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sun Feb 19 14:30:21 2023 +0100

    [CHANGE] UI fixes and SwiftUI previews for easier development

commit f206ee1406
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sun Feb 19 14:25:13 2023 +0100

    [ADD] Refresh all apps functionality in MyAppsView

commit 00dc9b36af
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sun Feb 19 11:40:26 2023 +0100

    [FIX] STDOUT output not visible in Xcode console

commit 24146cef90
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Feb 13 18:56:34 2023 +0100

    [ADD] LocalConsole showing STDOUT and STDERR

commit c46a50ec58
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sat Feb 4 14:35:58 2023 +0100

    [FIX] App compatibility info

commit de7e909c01
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sat Feb 4 14:29:02 2023 +0100

    [ADD] Debug entries for refresh attempts, sending feedback, advanced settings, and resetting the pairing file

commit fbc754d8b7
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sat Feb 4 13:07:04 2023 +0100

    [ADD] Error log view

commit 767d878051
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sat Feb 4 12:55:25 2023 +0100

    [FIX] Various UI issues

commit 132b140af2
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sat Feb 4 12:46:43 2023 +0100

    [ADD] App report button and trusted source badge in app detail view

commit df7d8871ff
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Feb 3 18:19:07 2023 +0100

    [FIX] AppIDsView and authentication workflow

commit ca2398e4c7
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Feb 3 18:16:48 2023 +0100

    [FIX] Full screen app screenshot previews

commit b8f02d2152
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Feb 3 14:58:06 2023 +0100

    [FIX] Accent color

commit e85876cd24
Author: Fabian Thies <git@fabian-thies.de>
Date:   Tue Jan 31 22:38:42 2023 +0100

    [CHANGE] Overhaul of the AppDetailView with version history, reviews & ratings, and app information

commit 3f06a53058
Author: Fabian Thies <git@fabian-thies.de>
Date:   Tue Jan 31 22:37:37 2023 +0100

    [UPDATE] AppPillButton dimensions and expiration text

commit 4ee053a4f9
Author: Fabian Thies <git@fabian-thies.de>
Date:   Tue Jan 31 22:35:09 2023 +0100

    [FIX] Show App IDs button only if user is logged in with their Apple ID

commit e5369524ce
Author: Fabian Thies <git@fabian-thies.de>
Date:   Tue Jan 31 22:32:11 2023 +0100

    [ADD] Load and show trusted sources with option to add them to the app

commit 77465cebd0
Author: Fabian Thies <git@fabian-thies.de>
Date:   Tue Jan 31 22:30:21 2023 +0100

    [ADD] Credits section in SettingsView

commit f90bf3bfcf
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Jan 16 21:23:16 2023 +0100

    [CHANGE] Extracted all strings into the Localizable.strings

commit 0000610b9d
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Jan 16 19:03:33 2023 +0100

    [FIX] Text alignment in SettingsView

commit c7e095583d
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Jan 16 19:02:58 2023 +0100

    [ADD] Hint for new users who don't have any sources

commit a725f3e9cc
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Jan 16 18:59:39 2023 +0100

    [ADD] AppScreenshot view with ImageProcessor to automatically rotate landscape screenshots

commit b5dea18073
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 13:37:38 2023 +0100

    [FIX] Issues introduced by changes to the AltSource specification.

commit b9b309e603
Author: Fabian Thies <github@fabian-thies.de>
Date:   Fri Jan 13 12:48:27 2023 +0100

    [UPDATE] Translations (#7)

    This PR merges all the new translations made on the SideStore weblate instance (https://translate.sidestore.io/projects/sidestore/app).

    New translations:
    - French
    - Korean

    Updated translations:
    - Spanish

    Co-authored-by: bogotesr <bogotesr@gmail.com>
    Co-authored-by: GABO1423 <35014183+GABO1423@users.noreply.github.com>
    Co-authored-by: Joss Laymon <71040782+bogotesr@users.noreply.github.com>
    Co-authored-by: mindfreakdev <shost212@gmail.com>
    Co-authored-by: Python <rjp2030@proton.me>
    Co-authored-by: Testi Cules <ervd516@gmail.com>

commit 15f1be0aa8
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 12:25:50 2023 +0100

    [FIX] Changes made by Xcode 14 after building the app

commit ffd80ce0b4
Author: Upal <shost212@gmail.com>
Date:   Mon Dec 26 19:18:33 2022 +0530

    Added Hindi Language (#5)

    * Added Hindi Language

commit 350891ee2a
Author: mindfreakdev <shost212@gmail.com>
Date:   Sun Dec 25 16:52:01 2022 +0530

    Added Dutch Language

commit 5dec1cd561
Author: mindfreakdev <shost212@gmail.com>
Date:   Sun Dec 25 12:30:42 2022 +0530

    Added Ukrainian Language

commit c4d235d742
Author: mindfreakdev <shost212@gmail.com>
Date:   Sun Dec 25 12:28:00 2022 +0530

    Added Ukrainian Language

commit cdc6675dd5
Author: Gabriel Morazán <35014183+GABO1423@users.noreply.github.com>
Date:   Sun Dec 25 01:08:47 2022 -0400

    Screen Crunch sucks

    Signed-off-by: Gabriel Morazán <35014183+GABO1423@users.noreply.github.com>

commit 85635bb26e
Author: GABO1423 <35014183+GABO1423@users.noreply.github.com>
Date:   Sun Dec 25 00:58:22 2022 -0400

    Spanish Translation Tweaks

commit 3be0a4a89c
Author: bogotesr <bogotesr@gmail.com>
Date:   Sat Dec 24 21:06:28 2022 -0700

    Add es-419 and finish adding support for the translations

    Added Latin American Spanish (probably not the best translation)

    Made everything reference the swiftgen stuff rather than having strings

commit 47e47fb3cf
Author: Fabian Thies <git@fabian-thies.de>
Date:   Thu Dec 22 10:29:57 2022 +0100

    [CHANGE] Extracted some example strings and replaced them by generated localized strings

commit 48903034b6
Author: Fabian Thies <git@fabian-thies.de>
Date:   Thu Dec 22 10:21:57 2022 +0100

    [ADD] SwiftGen configuration and generated files

commit 6952218ee7
Author: Fabian Thies <git@fabian-thies.de>
Date:   Thu Dec 22 10:10:58 2022 +0100

    [ADD] Empty Localizable.strings

commit 80146c1e03
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 12:04:10 2023 +0100

    [WIP] AppScreenshot view with ImageProcessor to automatically rotate landscape images. Possible through my fork of the AsyncImage framework.

commit 642ae996c9
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 12:02:56 2023 +0100

    [WIP] Fetch trusted sources in SourcesView

commit 8040636aa5
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 12:02:06 2023 +0100

    [WIP] AppIDs view in My Apps section

commit 731fcfaca7
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 12:00:00 2023 +0100

    [ADD] Badge in AppDetailView for apps from the official source and (WIP) trusted sources

commit 708fb3fccd
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 11:58:25 2023 +0100

    [ADD] Hint view in MyAppsView telling the user about where to find updates in the future if no updates are available

commit 9f429fb068
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 11:54:44 2023 +0100

    [FIX] App permission icon color

commit 29fc693f4d
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Jan 13 11:51:06 2023 +0100

    [ADD] Show source name and external url domain in NewsItemView

commit 6f373ad305
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Dec 23 16:02:57 2022 +0100

    [ADD] Full-screen app screenshot preview

commit c069d779d9
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Dec 23 15:21:16 2022 +0100

    [CHANGE] Replace system image name strings with SFSymbols

commit cd88970a22
Author: Fabian Thies <git@fabian-thies.de>
Date:   Fri Dec 23 13:12:39 2022 +0100

    [ADD] Dependency: SFSafeSymbols

commit 6b6708e43c
Author: Fabian Thies <git@fabian-thies.de>
Date:   Wed Dec 21 17:49:49 2022 +0100

    [ADD] WIP: Promoted category cards and app list filter button in BrowseView

commit 9206eeb9e3
Author: Fabian Thies <git@fabian-thies.de>
Date:   Wed Dec 21 17:48:45 2022 +0100

    [FIX] AccentColor in dark mode

commit 080bbb3c51
Author: Fabian Thies <git@fabian-thies.de>
Date:   Wed Dec 21 17:48:23 2022 +0100

    [ADD] Carousel for SideStore-specific announcements in NewsView

commit ea2c862900
Author: Fabian Thies <git@fabian-thies.de>
Date:   Wed Dec 21 17:45:44 2022 +0100

    [ADD] WIP: Add My Apps view with support for sideloading new apps, refreshing installed apps and much more

commit 4fe72ea113
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Dec 12 19:20:54 2022 +0100

    [CHANGE] Fixed the AppRowView background blur effect

commit c486a62b50
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Dec 12 19:20:10 2022 +0100

    [ADD] Backported dismiss() environment variable to let views dismiss themselves

commit 3ce4451da4
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Dec 12 19:18:57 2022 +0100

    [ADD] Search bar for BrowseView on iOS 15

commit 294ba12391
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Dec 12 19:16:36 2022 +0100

    [CHANGE] Fetch news when NewsView appears

commit 4a3343fe61
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Dec 12 19:15:16 2022 +0100

    Improved app detail view

commit d1e6ddd435
Author: Fabian Thies <git@fabian-thies.de>
Date:   Mon Dec 12 19:12:38 2022 +0100

    [ADD] Authentication view for connecting SideStore to an Apple ID

commit 3e0379dc70
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sun Nov 27 16:41:30 2022 +0100

    [WIP] Fixed the app permissions grid in AppDetailView

commit d99674f8bd
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sun Nov 27 16:17:08 2022 +0100

    [ADD] Expandable app and version description texts

commit ca7acc17da
Author: Fabian Thies <git@fabian-thies.de>
Date:   Sun Nov 27 00:26:15 2022 +0100

    [ADD] iOS 13 compatible AsyncImage implementation with cache

commit 16a8bce102
Author: Fabian Thies <git@fabian-thies.de>
Date:   Wed Nov 23 22:34:02 2022 +0100

    [ADD] News, Browse and Settings views ported to SwiftUI

    This commit contains WIP SwiftUI versions of most of the views in SideStore.
2023-05-20 11:31:25 -07:00
naturecodevoid
093e21799f fix: crash if save is called on non-unstable build 2023-05-20 10:48:52 -07:00
naturecodevoid
ad98ce43a9 fix warning 2023-05-20 10:47:55 -07:00
naturecodevoid
7f39d010b2 feat: merge #282 as an unstable feature 2023-05-20 09:51:45 -07:00
naturecodevoid
b6c9797104 Unstable Features groundwork 2023-05-20 09:24:09 -07:00
naturecodevoid
ed2270ff46 Anisette V3 (#324)
* initial anisette V3 implementation

* update V3 urls and log version

* fix crash where FetchAnisetteDataOperation.clientInfo would be nil when getting anisette V3 without provisioning first

* move adi.pb reset to its own button instead of doing it on sign out

* fallback to V1 if client_info fails

* make sure to unwrap optional strings

* feat(anisette): update v3 usage, improve error messages and names, report v3 errors to the user

* refactor(anisette): reduce duplicate JSON to anisette code

* fixes(anisette v3): improve errors, fix v3 server check, fix some edge cases where SideStore could crash and instead return an error, retry on -45061
2023-05-18 01:30:18 -07:00
SoY0ung
45b6c3b338 Fix 'The name for this app is invalid' error(#361)
Fix 'The name for this app is invalid' error when sideloading with non-ascii name ipa
2023-05-15 12:38:26 +08:00
SoY0ung
84e2284f56 Optimizing function calls
Thanks for @ktprograms advice
2023-05-14 19:06:22 +08:00
SoY0ung
1c0d0be622 Fix 'The name for this app is invalid' error
This error is related to App ID creation failure.
App ID name must be an ascii text. It is not allowed to create an App ID with non-ascii text like Chinese, Japanese.
If the name is NOT an ascii text, using bundleID instead.
2023-05-14 02:55:36 +08:00
naturecodevoid
a9ce0f487d fix: open Safari instead of force closing and provide a fallback for users with notifications disabled 2023-05-06 19:25:37 -07:00
naturecodevoid
07533e0365 fix: ensure minimuxer is started when refreshing in the background 2023-04-16 10:07:04 -07:00
naturecodevoid
ee5ddd4264 fix: use a notification instead of an alert for force close 2023-04-16 09:29:12 -07:00
naturecodevoid
f519d22d81 fix: removing _CodeSignature folder before resigning 2023-04-13 21:21:51 -07:00
naturecodevoid
51ed87086a [skip ci] ci: fully rename SideStore.ipa, even after extracting the artifact zip 2023-04-13 07:30:20 -07:00
naturecodevoid
1ca3aa3cdb fix: force close SideStore after 3 seconds if still reinstalling 2023-04-13 07:20:36 -07:00
naturecodevoid
0178c63f6a fix: hopefully reduce ApplicationVerificationFailed errors by removing _CodeSignature folders since those may cause a problem 2023-04-12 19:53:27 -07:00
naturecodevoid
8a97c409fa fix: add .AltWidget to app group ID when modifying for SideStore 2023-04-12 07:46:14 -07:00
naturecodevoid
3dd0735305 fix: always reinstall when refreshing ourselves 2023-04-11 21:50:15 -07:00
naturecodevoid
536f775baa Revert "Don't reinstall on first SideStore refresh"
This reverts commit 40e1225b87.
2023-04-11 21:12:01 -07:00
naturecodevoid
00f7a684a3 [skip ci] chore: rename tempEnt.plist to ReleaseEntitlements.plist to reduce future confusion 2023-04-11 21:09:32 -07:00
naturecodevoid
d79b166a6a chore: Remove old apps.json/app.json files 2023-04-11 21:05:53 -07:00
naturecodevoid
b3d827f56a refactor: remove minimuxerToOperationError in favor of extending MinimuxerError to be a LocalizedError and remove unused cases from OperationError 2023-04-11 21:04:07 -07:00
naturecodevoid
40bcef1dcb Use XYZ0123456 team ID for tempEnt.plist
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-04-06 20:45:18 -07:00
naturecodevoid
6146f1bdaa Update tempEnt.plist 2023-04-06 12:42:37 -07:00
f1shy-dev
f5d82d9ef0 Update tempEnt.plist to change AltStore to SideStore
Signed-off-by: f1shy-dev <56125930+f1shy-dev@users.noreply.github.com>
2023-04-06 20:23:27 +01:00
naturecodevoid
b2a29ae606 [skip ci] Include commit SHA in nightly version 2023-04-02 15:08:48 -07:00
naturecodevoid
98ccba53a2 [skip ci] Add version to artifact name
we can't do this for releases because some download URLs might rely on it being named SideStore.ipa

Build Info will also have version anyways
2023-04-02 08:01:57 -07:00
naturecodevoid
9bfda36647 [skip ci] Log version 2023-04-02 08:00:11 -07:00
naturecodevoid
5710cdf19c [skip ci] Fix PR commit suffix 2023-04-02 07:58:38 -07:00
naturecodevoid
20cf54bfcd [skip ci] Rename and move the first application groups log 2023-04-02 07:34:48 -07:00
naturecodevoid
2ce639e750 Remove app groups that contain AltStore 2023-04-01 20:03:15 -07:00
naturecodevoid
b1ed413c4f Revert Joelle's fix 2023-04-01 16:15:04 -07:00
naturecodevoid
b8c3060037 Log provisioning profile application groups 2023-04-01 16:10:40 -07:00
naturecodevoid
c3ea4940d7 Reduce duplicate consts 2023-04-01 16:10:05 -07:00
naturecodevoid
40e1225b87 Don't reinstall on first SideStore refresh 2023-04-01 16:09:28 -07:00
naturecodevoid
0c171122b2 refactor minimuxer to use swift-bridge (#321)
also add team ID to the end of the bundle ID for Debug builds to mirror SideServer
2023-04-01 16:02:12 -07:00
Joelle Stickney
6d0f4bb3da Fixes widget refreshing and is more thorough matching store ID 2023-03-28 23:48:24 -04:00
Joelle Stickney
5e2cc6e20c Update store check to check for AltServer or SideServer installation 2023-03-28 01:33:55 -04:00
naturecodevoid
99cb43bbea [skip ci] include commit SHA in PR builds
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-03-24 08:56:30 -07:00
Riley Testut
ca7d8277f7 Fixes “no provisioning profile with the requested identifier…” error
As of March 20, 2023, deleting an app’s auto-generated free provisioning profile is no longer supported. However, fetching the provisioning profile now re-generates is every time, so there’s no need to delete it first.

As a workaround, we now simply use the first profile we fetched if we receive an error when deleting it. This approach should continue to work even if Apple later reverses this change.
2023-03-21 18:52:56 -04:00
Joe Mattiello
337d26333e Update README.md
Signed-off-by: Joe Mattiello <mail@joemattiello.com>
2023-03-20 00:02:11 -04:00
Joe Mattiello
ebb64d255b Update README.md
Signed-off-by: Joe Mattiello <mail@joemattiello.com>
2023-03-20 00:00:56 -04:00
Joe Mattiello
7dcb199f68 Update README.md
Add repobeats svg

Signed-off-by: Joe Mattiello <mail@joemattiello.com>
2023-03-19 23:28:32 -04:00
naturecodevoid
4334e887de [skip ci] use bundle ID from Build.xcconfig in AltStore.xcconfig 2023-03-12 16:38:59 -07:00
naturecodevoid
4e84dc4cc8 [skip ci] rename the *.dSYM artifact so that macOS treats it as a normal folder 2023-03-07 08:24:28 -08:00
naturecodevoid
1a1ed072bf attach debugging symbols to actions builds 2023-03-07 07:50:31 -08:00
naturecodevoid
ae457f07c4 add https for ani.sidestore.io to Settings.bundle 2023-03-07 07:27:36 -08:00
Nythepegasus
00095942c3 Merge pull request #288 from SideStore/Remove-jk-anisette 2023-03-07 00:54:51 -05:00
Spidy123222
d1caa5fc21 Merge branch 'develop' into Remove-jk-anisette 2023-03-06 12:11:02 -08:00
Spidy123222
813e2f97ac Change version to 0.3.2
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-03-06 12:10:39 -08:00
Nythepegasus
bcb5a90f5e Add SSL encryption to ani.sidestore.io
Signed-off-by: Nythepegasus <nythepegasus84@gmail.com>
2023-03-06 14:09:16 -05:00
Spidy123222
020a1a3149 Replace sideloady to use sidestore ani
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-03-05 19:27:25 -08:00
Spidy123222
c4d649ec58 Remove jkcoxson anisette servers.
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-03-05 13:45:10 -08:00
naturecodevoid
c02cf2c284 Update error codes to match minimuxer error codes (this is why people got Unknown error instead of an actual error message)
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-03-04 08:08:35 -08:00
Spidy123222
c30afd042e Change version to 0.3.1
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2023-03-01 13:17:09 -08:00
naturecodevoid
17640fe6cf Cherry pick app ID logging fix from duplicate profiles PR 2023-02-25 14:36:37 -08:00
Joe Mattiello
2e4f6ee420 Merge pull request #272 from SideStore/naturecodevoid/prebuild-rust-deps-attempt-2
More actions updates, contributing guide
2023-02-24 00:00:43 -05:00
naturecodevoid
a3768d9221 [skip ci] actions: Add info/automate cache resetting 2023-02-21 17:27:56 -08:00
naturecodevoid
80c3390363 [skip ci] Makefile: Remove build_rust_dependencies 2023-02-21 17:07:58 -08:00
naturecodevoid
a5e3869d8f Project: update CONTRIBUTING.md to use Makefile 2023-02-21 17:01:24 -08:00
naturecodevoid
aa7d7c2d02 Revert "modify actions to work on test branch"
This reverts commit e59fb15926.
2023-02-21 12:51:34 -08:00
naturecodevoid
015f205569 update release descriptions 2023-02-21 12:42:56 -08:00
naturecodevoid
e59fb15926 modify actions to work on test branch 2023-02-21 12:24:25 -08:00
naturecodevoid
173c585f2d cleanup actions, revamp beta action, modify nightly build num system to be day specific 2023-02-21 12:23:12 -08:00
naturecodevoid
6f8c27793e cleanup makefile and add build steps from github actions 2023-02-21 12:19:08 -08:00
naturecodevoid
332b81c803 No more rust 2023-02-20 20:36:39 -08:00
naturecodevoid
4b343b500d fetch-prebuilt.sh whitespace improvement 2023-02-20 19:43:46 -08:00
naturecodevoid
e87c537642 Update CONTRIBUTING.md 2023-02-20 18:58:42 -08:00
naturecodevoid
2e6300cce2 add changes from attempt #1 2023-02-20 18:50:40 -08:00
naturecodevoid
09514d15a6 use prebuilt binaries 2023-02-20 18:48:21 -08:00
naturecodevoid
0de23dcba0 remove submodules 2023-02-20 16:33:11 -08:00
Joss Laymon
bacb153151 Use new pojav url
Signed-off-by: Joss Laymon <71040782+bogotesr@users.noreply.github.com>
2023-02-20 11:21:30 -07:00
naturecodevoid
a01aa299d8 SourcesViewController: Fix 1 trusted source causing an error making all trusted sources fail to load 2023-02-18 20:33:44 -08:00
Spidy123222
44edbddbd8 Replace placeholder video with instructions. (#266) 2023-02-15 21:14:51 -08:00
naturecodevoid
79d677cf3c Revamp issue and PR templates (#253)
* Create config.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Delete bug_report.md

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Create bug_report.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Create feature_request.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Create pull_request_template.md

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

---------

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-02-06 00:43:44 -05:00
oqammx86
be39b6512f Add sidestore anisette server
Signed-off-by: oqammx86 <84847714+oq-x@users.noreply.github.com>
2023-02-02 13:05:36 -05:00
naturecodevoid
fcfeea35da Revert "Release channel support (#239)"
This reverts commit 7d0eb8c61e.
2023-02-02 08:09:15 -08:00
naturecodevoid
7d0eb8c61e Release channel support (#239)
* Release channel support

- Show SideStore in Browse if it's not from the current SideStore source
- Change SideStore source URL and source ID based on if beta and nightly are in the version string
- Use StoreApp name for InstalledApp name to allow for source-specified name to show up in My Apps

* My Apps: Fix incorrect app name on first launch

* News: fix duplicate news items from multiple SideStore release channel sources

* Trusted Sources: Add stable and beta
2023-02-02 08:05:27 -08:00
naturecodevoid
4d8438a6b6 Update minimuxer 2023-02-01 20:05:26 -08:00
naturecodevoid
f611244e35 Add PR suffix to version in PR workflow
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-26 17:13:32 -08:00
naturecodevoid
546a978d3b Update .gitignore 2023-01-25 06:50:06 -08:00
naturecodevoid
70b23fb073 Merge remote-tracking branch 'upstream/develop' into develop 2023-01-19 17:39:11 -08:00
naturecodevoid
a56ca597d6 Fix build errors 2023-01-19 17:37:43 -08:00
naturecodevoid
679e0228a8 Remove debug
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-19 08:30:47 -08:00
naturecodevoid
e153394323 Merge branch 'develop' of https://github.com/naturecodevoid/SideStore into develop 2023-01-19 07:56:52 -08:00
naturecodevoid
5bd1fcfcfd Merge branch 'develop' of https://github.com/SideStore/SideStore into develop 2023-01-19 07:54:25 -08:00
naturecodevoid
2a392ddc44 SemVer version comparison 2023-01-19 07:52:47 -08:00
Joelle Stickney
b5cb8bc0d9 Updated Patreon Link 2023-01-18 14:41:24 -05:00
Joelle Stickney
fa170bcf98 Update README.md
Signed-off-by: Joelle Stickney <joellestickney+commit@gmail.com>
2023-01-13 10:39:12 -05:00
Joe Mattiello
7939d46949 Merge pull request #219 from SoY0ung/file_sharing
Reset Pairing File and checking minimuxer log in app
2023-01-12 20:35:06 -05:00
naturecodevoid
ab9df8201a Merge remote-tracking branch 'upstream/develop' into develop 2023-01-09 17:40:50 -08:00
SoY0ung
4a670ec091 You can check minimuxer log in Error Log Page 2023-01-09 16:17:00 +08:00
SoY0ung
10e57e59c4 You can access SideStore document from File App 2023-01-09 15:19:18 +08:00
SoY0ung
b9ec43ef34 Add 'Reset Pairing File' 2023-01-09 15:15:31 +08:00
SoY0ung
42197cd375 Fix Advanced Setting display error 2023-01-09 14:13:31 +08:00
jawshoeadan
704852973b Merge pull request #214 from jawshoeadan/remove-hardcoded-me
Update AltBackup.ipa to remove any hardcoded stuff
2023-01-07 18:02:56 -08:00
Jawshoeadan
056b4200df Update AltBackup.ipa 2023-01-04 21:55:00 -08:00
Joe Mattiello
250a7d8627 Merge pull request #213 from SideStore/feature/JoeMFixes
Joe M fixes for 1.0
2023-01-04 12:58:56 -05:00
Joseph Mattello
1ba51e161e Add placeholder for minimux retries
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-04 12:20:08 -05:00
Joseph Mattello
32e58af896 add workflow to attach builds to PR
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-04 09:54:29 -05:00
Joseph Mattello
312fa6fe76 final classes marked as final
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-04 09:52:12 -05:00
Joseph Mattello
afbe0837ba allow simulator to launch w/o pairing file
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-04 09:32:04 -05:00
Joseph Mattello
36ad2a720f log functions inlineable
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-04 09:31:51 -05:00
Joseph Mattello
901e3b14bb add final class to some classes
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-04 09:31:41 -05:00
Joseph Mattello
588d209f7b refs #160 codable feed structs
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-04 09:31:28 -05:00
naturecodevoid
554c54e6be Revamp actions to have stable, beta and nightly builds (#210)
* start on nightly builds

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update build.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update build.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update build.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update build.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update build.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update build.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Remove testing logic, final changes

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Nightly release build (#2)

* Update and rename build.yml to nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Create stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* trigger on tag

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* add version and build number

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* test

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Revert "test"

This reverts commit 9dff8d1d878a764a432ef4560300acdb4407313a.

* Remove pr from stable

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* add pr.yml

* Add nightly suffix and build number

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* add beta

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* [beta] test

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Remove test

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:55:14 -05:00
naturecodevoid
b0fac34ffc Nightly release build (#2)
* Update and rename build.yml to nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Create stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* trigger on tag

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* add version and build number

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* test

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Revert "test"

This reverts commit 9dff8d1d878a764a432ef4560300acdb4407313a.

* Remove pr from stable

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* add pr.yml

* Add nightly suffix and build number

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update stable.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* add beta

* Update nightly.yml

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* [beta] test

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

* Remove test

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>

Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
5ede9f7c6b Remove testing logic, final changes
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
c7254fd23e Update build.yml
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
55fcea04af Update build.yml
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
c212c0a6b2 Update build.yml
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
a31fd6709a Update build.yml
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
e367fd2b73 Update build.yml
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
1ca67d0241 Update build.yml
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
naturecodevoid
8ffa952ff9 start on nightly builds
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-04 08:54:21 -05:00
Joelle Stickney
da246fa30b Revert "Change default anisette server"
This reverts commit 25103c1188.
2023-01-04 08:52:59 -05:00
Joseph Mattello
13f306742e Revert "Merge pull request #189 from Nythepegasus/feature/retries"
This reverts commit 50841f5e24, reversing
changes made to d797ddd668.
2023-01-04 08:48:33 -05:00
Joelle Stickney
f3815dc45e Merge pull request #209 from SideStore/feature/deeplink_settings
Open Settings.app from SettingsVC
2023-01-01 12:33:31 -05:00
Joseph Mattello
d086254012 add button to open Settings.app in SettingsVC
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-01-01 12:20:08 -05:00
Spidy123222
bc4d5ba097 Fix sidestore version
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-12-30 19:07:17 -08:00
Spidy123222
c556783fe3 Bump version to 0.3.0 (#204)
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-12-30 15:48:15 -08:00
Joe Mattiello
5fba4c12aa Merge pull request #202 from SideStore/pullrequests/jawshoeadan/develop
Merge all of Riley's new error handling stuff REBASED
2022-12-30 17:10:45 -05:00
Joseph Mattello
7e0dde3ece fix 2 missing .c’s in libmobiledevice build
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 17:09:44 -05:00
Joseph Mattello
fc03e83531 fix wrong libframentzip.a link target
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 17:09:44 -05:00
Nythepegasus
4c441077c7 Add a bunch more "logging" throughout signing 2022-12-30 17:09:44 -05:00
Nythepegasus
4a5ca81e9a Show file extensions to help user choose file 2022-12-30 17:09:44 -05:00
Nythepegasus
75eebe8f8c Use right Bundle ID for AltWidget 2022-12-30 17:09:44 -05:00
Nythepegasus
271a8cdac5 Change the Bundle ID to always be SideStore 2022-12-30 17:09:44 -05:00
Nythepegasus
25103c1188 Change default anisette server 2022-12-30 17:09:44 -05:00
Nythepegasus
d81058e606 Add our own Analytics key 2022-12-30 17:09:44 -05:00
Nythepegasus
693df54b3b Change version to be 0.1.2 2022-12-30 17:09:43 -05:00
Riley Testut
ae6ed99dc4 Displays “TODAY” as section header for logged errors that occured that day 2022-12-30 17:09:15 -05:00
Riley Testut
14bd58e741 Prevents simultaneous database access from multiple AltStores
AltStore now sends a “WillAccessDatabase” notification before loading the persistent store, which causes other AltStore instances in memory to exit (either immediately, or upon returning to foreground).

This prevents multiple AltStore instances from simultaneously accessing the same database, which could result in corrupted data (especially if they used different database model versions).
2022-12-30 17:09:15 -05:00
Riley Testut
6d35a7a4ba Fixes widgets potentially not updating after refreshing apps 2022-12-30 17:09:15 -05:00
Riley Testut
46b0d1ceac Always displays PatreonViewController loading indicator when fetching patrons
Previously, we only showed the loading indicator if user had not yet cached any Friend Zone patrons.
2022-12-30 17:09:15 -05:00
Riley Testut
67a66d2fcd Fixes ErrorLogViewController’s dark mode appearance 2022-12-30 17:09:15 -05:00
Riley Testut
43e90b57ea [Apps] Updates AltStore beta to 1.6b2 2022-12-30 17:09:14 -05:00
Riley Testut
c80740e590 Updates LaunchViewController error alert to include more detail
Uses debugDescription over localizedDescription, because that makes it significantly easier to debug the underlying problem from a screenshot.
2022-12-30 17:09:14 -05:00
Riley Testut
54ccb9611e Fixes “error migrating persistent store” issue
We now set AppVersion.sourceID during migration, which fixes AppVersion entries conflicting across different Sources if multiple contain the same app + version.
2022-12-30 17:09:14 -05:00
Riley Testut
8fcb897800 [Apps] Updates AltStore beta to 1.6b1 2022-12-30 17:09:14 -05:00
Riley Testut
699eda5d1b [AltWidget] Adds “icon” style lock screen widget 2022-12-30 17:09:14 -05:00
Riley Testut
d7d0a83550 [AltWidget] Replaces ProgressRing with SwiftUI.Gauge 2022-12-30 17:09:14 -05:00
Riley Testut
e3c331c911 [AltServer] Fixes potential race condition crash when managing connections 2022-12-30 17:09:14 -05:00
Riley Testut
eda4dd6aec Updates app version to 1.6b1 2022-12-30 17:09:14 -05:00
Riley Testut
8ad7be474d Resolves AppVersion context-level conflict after migrating from Core Data model v10 2022-12-30 17:09:14 -05:00
Riley Testut
a64435f155 Migrates Core Data model from v10 to v11 2022-12-30 17:09:14 -05:00
Riley Testut
fa160124d2 Supports new “versions” key in source JSON
Allows sources to list multiple versions of an app.

Preserves backwards compatibility by assigning legacy version values when assigning AppVersions.
2022-12-30 17:09:14 -05:00
Riley Testut
5765cb8330 Adds AppVersion Core Data entity
Preserves redundant fields on StoreApp in database model for backwards compatibility.
2022-12-30 17:09:14 -05:00
Riley Testut
f472b227bb Automatically purges LoggedErrors older than one month
Occurs whenever app enters background.
2022-12-30 17:09:14 -05:00
Riley Testut
d2b419c42e Adds Error Log screen
Allows users to view a history of all errors that occured when performing app operations.
2022-12-30 17:08:49 -05:00
Riley Testut
09d4de660f Fixes CollapsingTextView incorrectly showing More button 2022-12-30 17:08:49 -05:00
Riley Testut
728dcd8523 Adds LoggedError Core Data entity
Allows us to save certain errors to disk so that they can be viewed again later from an error log.
2022-12-30 17:08:49 -05:00
Riley Testut
93cf9bf6a9 Makes AppProtocol.url optional
Allows us to create AnyApp values without a valid file URL.
2022-12-30 17:08:49 -05:00
Joe Mattiello
50841f5e24 Merge pull request #189 from Nythepegasus/feature/retries
Add various retries throughout the Rust function calls
2022-12-30 17:04:43 -05:00
Nythepegasus
fc6d92d1fc Add retries in the various minimuxer functions 2022-12-30 17:04:19 -05:00
Nythepegasus
7162a029bb Retry AFC 10 times before giving up 2022-12-30 17:04:19 -05:00
Joseph Mattello
d797ddd668 closes #93 redo of bundle id’s from .app
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 17:03:58 -05:00
Spidy123222
989e8c3aa6 Add SideKit integration (#149)
* Add a URL scheme

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Add sidestore as a scheme and fix spacing

* Undo formatting fix

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-12-30 17:03:18 -05:00
Joe Mattiello
08b79af242 Merge pull request #192 from LitRitt/new-ui-color
Change UI Color
2022-12-30 16:56:54 -05:00
LitRitt
0d2f346a30 Changes default app tint color
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-30 15:59:33 -05:00
LitRitt
39f1d5f5fd Probably does nothing but changed just in case
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-30 15:59:33 -05:00
LitRitt
05008bb7f8 Changes the color of navigations glyphs and colored text
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-30 15:59:33 -05:00
LitRitt
be90d6fc45 Changes settings highlight color
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-30 15:59:33 -05:00
LitRitt
a1bcdf9924 Change settings background color
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-30 15:59:33 -05:00
Joe Mattiello
b0e001393c Merge pull request #142 from SideStore/feature/cargo_in_xcode
Build Rust depends in XCode
2022-12-30 15:59:18 -05:00
Joseph Mattello
2d08941f6a udpate em_proxy.h
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 15:47:27 -05:00
Joseph Mattello
d0fef1f312 fix header paths
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 15:41:40 -05:00
Joseph Mattello
68342cb0d4 fix hardcoded paths in generated cargo xcodeprojs
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 15:28:03 -05:00
Joseph Mattello
2b419212a7 replace fragmentzip.a with xcodeproj
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 15:23:06 -05:00
Joseph Mattello
b2cbc7e34d github action please work
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 15:17:55 -05:00
Joseph Mattello
61247e575b fix build for updated submodules
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 15:17:47 -05:00
Joseph Mattello
31e18266d1 build.yml updates formatting and xcode ver
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 14:59:16 -05:00
Joseph Mattello
df8a8de889 update libplist to head
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 14:57:54 -05:00
Joseph Mattello
8a037d6b29 update libmobiledevice to head
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 14:57:05 -05:00
Joseph Mattello
47b555b98c project: fix wrong paths for depends
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 14:56:52 -05:00
Joseph Mattello
0c2dae475e cargo: fix github action?
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 14:49:13 -05:00
Joseph Mattello
dc676d04d8 Add rust depends to xcode build
Signed-off-by: Joseph Mattello <mail@joemattiello.com>

cargo script for action

fixes path

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

cargo.sh More robust env for xcode cli

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

rust: add xcode projs made with cargo-xcode

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-30 13:21:57 -05:00
Joelle Stickney
15b54bff50 Merge pull request #201 from SideStore/Add-Community-Store-Trust
Add Community store to Trusted Sources
2022-12-29 01:09:51 -05:00
Spidy123222
47bd4b4c0b Update trustedapps.json
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-12-28 23:50:24 -05:00
Joelle Stickney
3c8b36ddfe Merge pull request #200 from SideStore/Add-Yattee-Source
Add Yattee to trusted sources
2022-12-25 05:42:05 -05:00
Spidy123222
608df3fddd Add Yattee to trusted sources
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-12-25 02:22:06 -08:00
Joelle Stickney
c092c285ee Removed Jackson from CODEOWNERS
Due to his two year mission trip and the fact he, as an codeowner, gets added to SideStore issues / PRs automatically, it seemed best to remove him atm.
2022-12-24 18:19:17 -05:00
Joelle Stickney
93b745e379 More credit adjustments 2022-12-24 17:19:55 -05:00
Joelle Stickney
c18db77ade Turn off all background refresh notifications
This removes all background refresh notifications but keeps the "Reminder to open SideStore every so often", and the "x app expires soon" local push notifications.
2022-12-19 23:26:15 -05:00
Joelle Stickney
2c0b167e6b Adjusted credits 2022-12-19 07:36:44 -05:00
Joelle Stickney
313254d0c8 Credit text changes 2022-12-19 06:40:10 -05:00
f1shy-dev
6f519c97d3 Fix .mobiledevicepairing files (#186)
* Fix .mobiledevicepairing file selection and also add support for .xml pairing files

* Add a more meaningful message for when pairing file selection is cancelled.
2022-12-17 14:13:53 -07:00
LitRitt
17a3e16b1d Change browse icon (#188)
Now matches new icon

Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>

Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-17 14:13:38 -07:00
Joseph Mattello
8199358088 Info.plist add LSSupportsOpeningDocumentsInPlace 1
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-17 04:29:10 -05:00
Joseph Mattello
412928eeaa AltWidget version set to $(MARKETING_VERSION)
fixes xcode warning that version of app and extension don’t match

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-17 04:28:53 -05:00
Joseph Mattello
51e1b935bd fix 3 more style .white deprecations to .medium
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-17 03:13:30 -05:00
Joseph Mattello
742b51e5e2 fix deprecated style .white to .medium
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-17 03:12:13 -05:00
Joseph Mattello
fdb5e2eebb fix xcode warning
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-17 03:11:22 -05:00
Joseph Mattello
0192f64cd2 Add AltStore Release scheme
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-17 03:10:45 -05:00
Joseph Mattello
193298ac87 fix crash for missing patreon images
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-12-17 03:10:28 -05:00
Joe Mattiello
a81cb81799 Merge pull request #181 from LitRitt/change-designer
Update app icon and designer
2022-12-11 21:56:58 -05:00
LitRitt
ad8a7fdc9b Update Settings.storyboard
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-11 15:54:18 -08:00
LitRitt
5440afcebe Update SettingsViewController.swift
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-11 15:54:18 -08:00
LitRitt
715d7e664c Update App Icon
Signed-off-by: LitRitt <78584620+LitRitt@users.noreply.github.com>
2022-12-11 15:54:18 -08:00
jawshoeadan
aa182cfa68 Merge pull request #179 from jawshoeadan/beta-apps
Fix beta apps on the my apps view, and fix deactivating/activating apps
2022-12-11 16:43:46 -06:00
Jawshoeadan
f92dd7a872 Remove all restrictions based on Patreon account (hopefully) 2022-12-11 15:41:43 -06:00
Jawshoeadan
b02b9197d0 Fix beta apps on the my apps view, and fix deactivating/activating apps 2022-12-08 19:24:28 -06:00
Jawshoeadan
86d02be70c Remove Patreon check to show beta apps in store view 2022-12-08 10:28:34 -06:00
Spidy123222
cb990978ee add pokemmo into trusted sources. (#176) 2022-12-07 16:36:17 -08:00
Joshua Laymon
a103202c92 Merge pull request #171 from bogotesr/settings
Better anisette settings
2022-12-05 11:39:08 -07:00
bogotesr
9d7b133037 Add crystal's server 2022-12-04 15:43:08 -07:00
bogotesr
f727f2a1a9 describe what the toggle does 2022-12-03 21:19:31 -07:00
bogotesr
03034768d9 Better anisette settings
adds multivalue selector for some anisette servers
2022-12-03 13:48:33 -07:00
Joe Mattiello
aed3e20e08 Merge pull request #164 from SideStore/update-em-submodules 2022-12-01 20:02:04 -05:00
JJTech0130
74bac6d986 update em_proxy and minimuxer submodules 2022-11-30 18:17:01 -05:00
Fabian Thies
7ebecc353a Add missing App Permission Types (#159)
* Add missing app permission types

* Remove old unused icons for photos, background fetch and background audio permission types

* Add missing icons for contacts and reminders permission types

* Add missing camera permission icon and name

* Switch permission icons to filled versions for a more cohesive look
2022-11-29 06:30:51 -07:00
Jackson Coxson
f0302b0d1e Add an issue template (#157)
Update issue templates
2022-11-26 19:34:52 -07:00
Spidy123222
0b004ad089 Update app.json
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-22 21:54:37 -08:00
Joe Mattiello
c9001f068b Merge pull request #143 from SideStore/feature/fix_xcconfig_widget 2022-11-23 00:01:33 -05:00
Joseph Mattello
96e0554aae Fix xcconfig vars for widget
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-22 23:06:45 -05:00
Joe Mattiello
31b4aadaba Merge pull request #141 from SideStore/Version
Bump version to 0.1.1
2022-11-22 22:22:49 -05:00
Spidy123222
f46fa5392a Change wording.
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-22 22:20:37 -05:00
Spidy123222
3b6a17f193 For old client give message update
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-22 22:20:37 -05:00
Spidy123222
aea77d3b8c Update project.pbxproj
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-22 22:20:37 -05:00
Spidy123222
7cfbe077db Update Build.xcconfig
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-22 22:20:37 -05:00
Joe Mattiello
7bb620f941 Merge pull request #122 from SideStore/pullrequests/bogotesr/develop
Rebase of #62
2022-11-22 22:18:10 -05:00
Joseph Mattello
5b0341a733 Add TODO notes for PR #122 notes
what to do with patreon table view and url

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-22 22:16:56 -05:00
Joshua Laymon
d99225da1f Add files via upload
Signed-off-by: Joshua Laymon <71040782+bogotesr@users.noreply.github.com>
2022-11-22 22:16:56 -05:00
Spidy123222
f279180a37 Update AltStore.xcconfig
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-22 22:16:56 -05:00
Spidy123222
9a22018477 Try fixing compile error
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-22 22:16:56 -05:00
Spidy123222
197119e56d Remove mention of sideserver in app 2022-11-22 22:16:56 -05:00
Joe Mattiello
dd055ddc5d Merge pull request #45 from SideStore/feature/noserver
Remove SideServer from monorepo
2022-11-22 22:06:30 -05:00
Joseph Mattello
94c3277245 Remove Server from xcode proj
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-22 21:35:34 -05:00
Joseph Mattello
fdaf402472 Remove Server files/folders
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-22 10:13:23 -05:00
Joe Mattiello
bce7764f75 Merge pull request #127 from SideStore/pullrequests/jkcoxson/develop
Anisette URL - Pullrequests/jkcoxson/develop
2022-11-22 10:11:51 -05:00
Joseph Mattello
70a258aae2 AnisetteManager UserDefaults.standard from .shared
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 21:24:40 -05:00
Joseph Mattello
77cf00e8e4 info.plst add mobiledevicepairing imported type
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
de05579d1f Remove customAnisetteURL empty string default
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
c7d4b722d0 refactor anisette manager to own file
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
02b837c54b Settings.bundle group and text type URL
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
50e0e88cc2 ORGANIZATIONNAME to SideStore
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
c34245ff21 FetchAnisetteDataOperation.swift fix url reading
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
f1a8334f59 OSLog+SideStore fix staticstring
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
c8fc4ea500 AltStore scheme XCode auto edits
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
39805bc103 Anisette URL fix missing abort
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
2c615682df OSLog+SideStore update copyright
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Joseph Mattello
1257e4efac Anisette URL convenience methods and logging
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-21 20:54:55 -05:00
Jackson Coxson
5e4a21087e Remove customAnisetteURL from default Info.plist 2022-11-21 20:54:55 -05:00
Jackson Coxson
2aaef99a54 Choose a pairing file at runtime 2022-11-21 20:54:55 -05:00
Jackson Coxson
161d3a795d Add settings bundle 2022-11-21 20:54:55 -05:00
Jackson Coxson
9b671cb1a9 Add default fields for custom anisette 2022-11-21 20:54:55 -05:00
Jackson Coxson
07d9a9f2c3 Set anisette URL from plist value 2022-11-21 20:54:55 -05:00
Spidy123222
efabe7f536 Reorder trusted sources
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-21 20:24:57 -05:00
Joe Mattiello
82aead976e Merge pull request #128 from Spidy123222/Fix/signing-bundleid
Fix Refreshing sidestore and staging error.
2022-11-18 21:25:42 -05:00
Spidy123222
17be52c7b6 Fix refreshing to use normal bundleid
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-17 18:16:21 -08:00
Spidy123222
d0a196ec40 Update Bundle+AltStore.swift
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-17 17:08:58 -08:00
Spidy123222
d484de185d Update AltStore.xcconfig
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-17 17:08:11 -08:00
Spidy123222
96e4e7a4e8 Update Build.xcconfig
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-17 17:07:48 -08:00
jawshoeadan
4adb34b959 Update README to include Rust specific build instructions (#124)
Update README.md

Signed-off-by: jawshoeadan <62785552+jawshoeadan@users.noreply.github.com>

Signed-off-by: jawshoeadan <62785552+jawshoeadan@users.noreply.github.com>
2022-11-16 14:14:41 -07:00
Spidy123222
819bc12a68 Change version on to reflect being pre-release (#123)
* Change source url and version in app.json

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Change app.json to reflect SideStore/apps.json

* Change the source to be future default

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
Co-authored-by: Nythepegasus <nythepegasus84@gmail.com>
2022-11-15 22:23:30 -07:00
Jackson Coxson
eb23e5365f Show error message when pairing file is missing (#118)
* Show alert on startup error

* Call super to load normal view
2022-11-13 18:40:33 -07:00
Joe Mattiello
84e2faf8a8 [#102][#101] Loading view error handling, Load pairingfile from docs, bundle, plist (#112)
* refs #102 load pairingfile from docs, bundle,plist

tries Documents/ALTPairingFile.plist, then app bundle resources ALTPairingFile.plist, finally Info.plist with non default value

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

* refs #102 change plist to mobiledevicepairing

fixes xcode compiling .plist resources

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-08 12:15:09 -07:00
Joe Mattiello
84f58efc17 Add .editorconfig file (#109)
Signed-off-by: Joseph Mattello <mail@joemattiello.com>

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-08 12:10:39 -07:00
Joe Mattiello
42254ee4a1 Add github CODEOWNERS file (#108)
* Add .github/CODEOWNERS

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

* Edit CODEOWNERS add jkcoxson

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-08 12:09:47 -07:00
Joe Mattiello
7c564aed7a Create Makefile (#99)
* add Makefile

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

* Makefile fix leftover test code

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-11-08 12:09:33 -07:00
Spidy123222
8cdcb29274 Change version to 1.0.0 (#96)
* Change app list to 1.0.0

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Change build version.

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* change more to 1.0.0

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-06 01:38:43 -06:00
Joshua Laymon
403a369df9 Match branding some more (#72)
* change app name to SideStore
* Make it P U R P L E
* Fix actions for branding
* Change patreon description in settings
* Update RefreshAttemptsViewController.swift
* Update LaunchViewController.swift
* Update some of Credits
* Fix space
* More Branding fixes
* Change How it Works to have better wording.
* Change Branding for source and bundle
* Get the Rest of the Branding and popup naming fixes
* Welcome to SiteStore
* Update instructions to not include sideserver 

This removes sideserver fro the instructions on how it works as sideServer in background of a computer so far isn’t needed.
* Remove mention of sideserver in the app
* SideStore error Message instead of AltServer
* some adjustments
* forgot to add this in the last commit
* Try fixing compile error
* Spell correct of last commit
* Fix Requested changes 11/5/2022

Signed-off-by: bogotesr <71040782+bogotesr@users.noreply.github.com>
Co-authored-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-05 23:50:07 -07:00
Spidy123222
5527912cd1 Fix secret tunnel header to em proxy
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-05 16:17:23 -07:00
JJTech
c8531dfe37 Update README.md (#90)
* Update README.md

Signed-off-by: JJTech <jjtech@jjtech.dev>

* Add minimuxer to list of libraries in read

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Change wording for minimuxer

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Change Secret tunnel to be em-proxy and add detail

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

Signed-off-by: JJTech <jjtech@jjtech.dev>
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
Co-authored-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-05 16:53:09 -06:00
Spidy123222
ed8bb2e5a1 Add pojavlauncher to trusted sources (#107)
* Add Pojavlauncher source

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update trustedapps.json

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update trustedapps.json

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update FetchTrustedSourcesOperation.swift

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update trustedapps.json

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update FetchTrustedSourcesOperation.swift

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-05 16:50:35 -06:00
Jackson Coxson
dd66355488 Implement emotional damage (#95)
* Implement em_proxy

* Update libimobiledevice

* Add minimuxer library to Xcode

* Build missing C files for libimobiledevice

* Remove objective C library

* Add pairing file to Info.plist

* Heartbeat self on startup

* Enable JIT on-device

* Implement on-device installation

* Fix OpenSSL header errors

* Random submodule bullcrap go

* Search release folder for emotional damage

* Clean dependencies

* Build Rust dependencies attempt 1/999

* Update em_proxy

* Implement refreshing apps

* Clean up old operations

* Remove all AltServer code

* Remove files from Xcode project

* Implement auto mounting the developer DMG

* Recover from app being backgrounded

* Fixed keeping pairing file in app after updating SideStore (#3)

* Use compliant error handling for minimuxer

* Fix app failing to install

* Don't kill proxy on backgrounding

* Makes sure the ALTPairingFile gets transferred even if team IDs change (#4)

* Step 1 to allow SideStore to resign itself

* Update ResignAppOperation.swift

* Adding cache for action runner (#5)

* Start caching commit for actions

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update build.yml

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update build.yml

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Use rust lib directories to cache

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Cache cargo also

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Fix spacing

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Replace cargo id for caching

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Remove cache if statements

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Add disconnected WireGuard detection

* Add minimuxer logging

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
Co-authored-by: jawshoeadan <62785552+jawshoeadan@users.noreply.github.com>
Co-authored-by: Joelle Stickney <joellestickney@gmail.com>
Co-authored-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-11-02 18:58:59 -06:00
Joshua Laymon
fc3f83231c Use a custom default source for SideStore (#71)
* Adds a Custom default source and allows Updating app via ota.

Co-authored-by: Spidy123222
2022-10-18 01:10:18 -07:00
Spidy123222
e70c712020 README: Make app names in compile use Side names (#89)
Make app names in compile steps use Side names
2022-09-27 11:48:13 -07:00
jawshoeadan
1b34aeaec4 Get anisette from HTTP GET request instead of AltServer (#87) 2022-09-16 11:52:53 -06:00
Spidy123222
2566bfa2ed Make Sidestore use its own trustedapps json (#58)
* Add trusted apps list json

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Force to use apps.json

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Rename apps.json to trustedapps.json

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Redo trustedlink

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Update FetchTrustedSourcesOperation.swift

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

* Add provenance-emu source

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-09-14 13:24:04 -07:00
Spidy123222
ba06f2bbc6 Revert "Revert "Change settings altstore names to SideStore"" (#82) 2022-09-14 04:45:33 -07:00
Spidy123222
1e4fe1680f Merge pull request #81 from SideStore/revert-61-Change-settings-AltStore-names-to-side
Revert "Change settings altstore names to SideStore"
2022-09-14 04:39:01 -07:00
Spidy123222
2effb199a1 Revert "Change settings altstore names to SideStore" 2022-09-14 04:38:34 -07:00
Spidy123222
23c139320a Merge pull request #61 from Spidy123222/Change-settings-AltStore-names-to-side 2022-09-14 04:37:07 -07:00
Spidy123222
f65eba606e Revert Patreon Description to avoid conflict.
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-09-14 04:27:26 -07:00
Spidy123222
7a2825da9a Remove patreon footer description
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-09-14 04:19:56 -07:00
Spidy123222
2975eddfe9 Merge branch 'develop' into Change-settings-AltStore-names-to-side 2022-09-13 22:58:07 -07:00
Spidy123222
7f28eae954 Change Xcode version to 14.0.0 (#79)
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-09-13 21:23:46 -06:00
JJTech
789be5e942 Merge branch 'develop' into Change-settings-AltStore-names-to-side 2022-09-01 19:15:31 -04:00
JJTech
85b114cdfd Bump minimum Xcode version to Xcode 14 (#64)
* Remove Xcode 13 from the build matrix
* Bump minimum Xcode version to Xcode 14
Signed-off-by: JJTech <jjtech@jjtech.dev>
2022-09-01 17:59:50 -04:00
Spidy123222
53d063e994 Merge branch 'SideStore:develop' into Change-settings-AltStore-names-to-side 2022-08-31 13:24:57 -07:00
Spidy123222
0ab081ccbc Merge pull request #52 from jawshoeadan/develop
Adds fakesign and sets default team id to be com.SideStore to fix AltServer sideloading
2022-08-31 13:14:36 -07:00
Spidy123222
00ce9d64dc Merge branch 'develop' into develop
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-08-31 11:25:28 -07:00
Spidy123222
be20c024aa Change wording of accessing local traffic
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-08-31 11:15:52 -07:00
Spidy123222
992fb9839a Update SettingsViewController.swift
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
2022-08-31 05:11:15 -07:00
JJTech
96ae2ee7ac Convert to build matrix (#56)
* Convert to build matrix
* turn off fail-fast strategy
we don't want all the jobs cancelled just because it didn't build in an old version of Xcode
* only trigger push build when pushing to master or develop

Signed-off-by: JJTech <jjtech@jjtech.dev>
2022-08-23 18:13:20 -04:00
JJTech
379cecb08f Use Xcode 14 (#55)
New iOS 16 features require Xcode 14
Xcode 14 is only available on macOS 12 runners

Signed-off-by: JJTech <jjtech@jjtech.dev>
2022-08-19 11:35:31 -04:00
Nythepegasus
9089b271b3 Merge remote-tracking branch 'refs/remotes/upstream/develop'
Conflicts:
	AltStore.xcodeproj/project.pbxproj
	AltStore/AppDelegate.swift
2022-08-19 01:14:16 -04:00
Josh-WikiRealty
874da8c8d6 Generalized everything to match SideStore branding for workflow
Sideloading now works with AltServer
2022-07-04 17:30:50 -07:00
Josh-WikiRealty
989580d196 Fakesign app in workflow 2022-07-03 01:03:48 -07:00
Josh-WikiRealty
03ef54c37b Add entitlements file and update gitignore 2022-07-03 00:53:02 -07:00
jawshoeadan
ab56dda275 Merge branch 'SideStore:develop' into develop 2022-07-02 13:38:30 -07:00
JJTech
3a91f958e3 Add build badge, fix PR badge style (#50)
* Add build badge, fix PR badge style

Signed-off-by: JJTech <jjtech@jjtech.dev>

* Badge links HTTPS

Signed-off-by: JJTech <jjtech@jjtech.dev>
2022-06-23 11:47:17 -04:00
JJTech
79913a0c9c Put back Package.resolved, have CI remove it (#49)
* Remove swift resolved packages from git ignore

* Add Package.resolved to

* Have CI remove Package.resolved
2022-06-23 11:47:01 -04:00
JJTech
d0fe64ecfa Fix CI (#47)
* Try downgrading to macOS 11

Signed-off-by: JJTech <jjtech@jjtech.dev>

* Delete Package.resolved

Signed-off-by: JJTech <jjtech@jjtech.dev>

* Add Packagage.resolved to .gitignore

Signed-off-by: JJTech <jjtech@jjtech.dev>
2022-06-22 10:29:05 -04:00
JJTech
4da69685a1 Add InteliJ to git ignore (#44)
Ignore AppCode/InteliJ .idea folder
2022-06-21 13:39:46 -06:00
jawshoeadan
bf560dd10d Merge branch 'SideStore:develop' into develop 2022-06-20 18:18:05 -07:00
jawshoeadan
7ce76ee28d Add CI/CD workflow (#43) 2022-06-20 15:23:05 -06:00
jawshoeadan
540e9bad29 Merge branch 'SideStore:develop' into develop 2022-06-20 14:21:33 -07:00
Josh-WikiRealty
22c2e2c4e5 Add CI/CD workflow 2022-06-20 14:18:34 -07:00
JJTech
f67d9dcdfa Convert AltSign and OpenSSL into proper Swift packages (#42)
Convert AltSign and OpenSSL to Swift packages
2022-06-20 14:27:28 -06:00
JJTech
edfcadcbdc Merge pull request #26 from JJTech0130/patch-4
Make it more consistent
2022-06-20 16:15:53 -04:00
Joe Mattiello
156bcc7d54 Rename AltStore with variables (#17)
* iOS 14 for xcode 14 errors

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

* add group.APP_GROUP to more plists

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

* reorder altid groups

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

* update to newer sparkle api

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

* fix warnings in altsign and libmobdevice

Signed-off-by: Joseph Mattello <mail@joemattiello.com>

Co-authored-by: JJTech <jjtech@jjtech.dev>
2022-06-20 13:13:15 -06:00
Nythepegasus
63ff912d76 Merge pull request #27 from rileytestut/develop
Pull from Upstream
2022-06-13 18:47:32 -04:00
JJTech
6cbfaac4f4 Make it more consistent
Signed-off-by: JJTech <jjtech@jjtech.dev>
2022-06-13 18:39:34 -04:00
JJTech
5574172d99 Replace xcodeworkspace with xcodeproj (#20) 2022-06-08 17:38:36 -06:00
JJTech
bc8081ebae Update build instructions (#19)
* Replace redundant step with recursive clone

See https://stackoverflow.com/a/4438292

* Fix code signing instructions, formatting

* Clarify step 3 (embedding the UDID)
2022-06-08 17:32:02 -06:00
JJTech
6ed6132c54 Replace redundant text with link (#18) 2022-06-08 17:13:25 -06:00
Joe Mattiello
76c02c98d8 Merge pull request #16 from SideStore/feature/XCConfig2
XCConfig for bundle/sign ids
2022-06-07 08:14:48 -04:00
Joseph Mattello
012a7885ff xcode touching packages
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 06:10:23 -04:00
Joseph Mattello
2b3d41d982 refactor sparkle URLs to info.plist
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 06:10:13 -04:00
Joseph Mattello
a56a48145b spm touches
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 06:01:27 -04:00
Joseph Mattello
8dc097e23c AltSign remove warning
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 06:01:27 -04:00
Joseph Mattello
0323520389 Remove workspace requirement for xcodeproj
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 06:01:27 -04:00
Joseph Mattello
e1e395023d AltStore & AltServer builds with XCConfig
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 06:01:27 -04:00
Joseph Mattello
850214b103 Use XCConfig files for codesign and bundle ids
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 06:01:26 -04:00
Joe Mattiello
02e9805482 Merge pull request #15 from SideStore/feature/carthage2
Remove Cocoapods for Swift Packages
2022-06-07 06:00:07 -04:00
Joseph Mattello
0feae8402e delete unused podfiles
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 05:22:17 -04:00
Joseph Mattello
042da53b54 temp comment out Sparkle.app codesign
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 04:02:22 -04:00
Joseph Mattello
aa1b2bace7 oops
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 04:02:22 -04:00
Joseph Mattello
6b1b4d6015 altserver depends to swiftpm
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 04:02:22 -04:00
Joseph Mattello
ebeac417e5 AltStore.app works with SwiftPM
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 03:31:33 -04:00
Joseph Mattello
be005616ea altstore app target ios only
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 03:15:54 -04:00
Joseph Mattello
ec3a9b0615 altstorecore builds using forked altsign
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 03:14:54 -04:00
Joseph Mattello
0b3e651c4b keychain access as swift module
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 03:14:34 -04:00
Joseph Mattello
426bdd3aa1 remove superfluous code signing on framework
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 02:36:43 -04:00
Joseph Mattello
75e29d61f8 altsorecore macOS only
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 02:33:52 -04:00
Joseph Mattello
dfab283154 libimobiledevice submodule fork change
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 02:30:03 -04:00
Joseph Mattello
986c0d7edc de-intergrate cocoapods
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 02:11:28 -04:00
Joseph Mattello
918c44bc89 altserver target macos only
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-07 02:11:28 -04:00
Joe Mattiello
b8f680d74a Merge pull request #14 from SideStore/feature/MailRelaunch
Check if mail is open b4 launch
2022-06-07 02:02:19 -04:00
Joseph Mattello
76fcf6d545 mail.app only launch if not open
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-06 23:41:58 -04:00
Joe Mattiello
c51d25c58b Merge pull request #4 from SideStore/feature/multiPlatform
Multi-platform app target support (tvOS, watchOS etc)
2022-06-03 23:36:34 -04:00
Joseph Mattello
1efdba096c AltStoreCore multi-platform URLs
Signed-off-by: Joseph Mattello <mail@joemattiello.com>

apps.json add platform url support

Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-03 23:36:13 -04:00
Joe Mattiello
c4505b7c42 Merge pull request #13 from SideStore/pullrequests/jawshoeadan/master
Automatically open mail when fetching Anisette data and update error …
2022-06-03 23:35:40 -04:00
Jackson Coxson
d5235bd40b Merge branch 'rileytestut:develop' into develop 2022-06-03 17:21:51 -06:00
Josh-WikiRealty
353d105c04 Automatically open mail when fetching Anisette data and update error message to accomodate 2022-06-02 02:03:11 -04:00
Joe Mattiello
070cb6c873 Merge pull request #12 from jkcoxson/develop
Add multi team view
2022-06-02 02:00:35 -04:00
Joseph Mattello
a066dda0f9 update gitignore
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-06-02 00:29:21 -04:00
Jackson Coxson
ac929c2603 Add team view controller to Xcode's list of files 2022-05-31 00:02:57 -06:00
Jackson Coxson
9102402a18 Prefer local servers over VPN 2022-05-30 23:53:22 -06:00
Jackson Coxson
54a0fc21d8 Merge branch 'SideStore:develop' into develop 2022-05-30 23:44:26 -06:00
Jackson Coxson
e2b8b7369e Add view for multiple teams from Megarush 2022-05-30 23:43:47 -06:00
Spidy123222
bcfbe515a4 Change Readme (#11)
* Update README.md

* change casing of SideStore

* Add roxas and replace git

* Additional detail for Netmuxd

* fix wording

this fixes the wording of Licensing and building instructions to be simplified.

* Correct spelling AltStore

* Forgotten S
2022-05-30 23:14:00 -06:00
Jackson Coxson
46834ab5ce Revert "Change AltStore naming to SideStore"
This reverts commit 646000920f.
2022-05-30 22:54:21 -06:00
Jackson Coxson
646000920f Change AltStore naming to SideStore 2022-05-30 22:46:21 -06:00
Jackson Coxson
5ea83ccea1 Change icon and colors 2022-05-30 22:05:26 -06:00
Jackson Coxson
03c6473685 Merge branch 'rileytestut:develop' into develop 2022-05-30 11:26:13 -06:00
Jackson Coxson
d37890fac4 Add manual connection (#10)
* Add manual connection

* Send request to netmuxd for a manual connection
2022-05-30 11:25:54 -06:00
Joe Mattiello
d5057ea8ea AltStore add iPad to device family (#5)
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-05-30 10:51:10 -06:00
Joe Mattiello
2cbebbe9b7 WiFi to Wi-Fi spelling (#6)
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2022-05-30 10:50:03 -06:00
1131 changed files with 19649 additions and 47735 deletions

39
.editorconfig Normal file
View File

@@ -0,0 +1,39 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8# 4 space indentation
# Swift files
[*.swift]
indent_style = space
indent_size = 4
charset = utf-8# 4 space indentation
# 4 space indentation
[*.py]
indent_style = space
indent_size = 4
# Tab indentation (no size specified)
[Makefile]
indent_style = tab
# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @JoeMatt @lonkelle

40
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Bug Report
description: Report a bug
title: "[BUG] "
labels: ["bug"]
assignees:
- naturecodevoid
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! Before you continue filling out the report, please **[search in GitHub Issues](https://github.com/SideStore/SideStore/issues?q=is%3Aissue+is%3Aopen) for the bug you are experiencing** in case it has already been reported.
**Please use [Discord](https://discord.gg/RgpFBX3Q3k) or [GitHub Discussions](https://github.com/SideStore/SideStore/discussions) for support.**
- type: textarea
id: description
attributes:
label: Describe the bug
description: What is the bug and how did you discover it?
placeholder: Please be clear and concise with your description.
validations:
required: true
- type: textarea
id: how-to-reproduce
attributes:
label: Instructions to reproduce
description: Please include clear and consistent instructions for reproducing the bug to make it easier for us to fix it.
validations:
required: true
- type: input
id: app-version
attributes:
label: What version of SideStore are you using?
description: To retrieve this, go to `Settings` in the SideStore app and scroll down to the bottom.
validations:
required: true
- type: textarea
id: other-info
attributes:
label: Other info
description: If you have any other comments, other info that might be useful, or if you found a workaround, please put it here.

10
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# force issue template usage
blank_issues_enabled: false
contact_links:
- name: Discord
url: https://discord.gg/RgpFBX3Q3k
about: If you need support, please go here first instead of making an issue!
- name: GitHub Discussions
url: https://github.com/SideStore/SideStore/discussions
about: As an alternative to Discord, you can also make a new GitHub discussion.

View File

@@ -0,0 +1,33 @@
name: Feature Request
description: Suggest a feature
title: "[FEATURE REQUEST] "
labels: ["enhancement"]
assignees:
- naturecodevoid
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request! Before you continue filling out the form, please **[search in GitHub Issues](https://github.com/SideStore/SideStore/issues?q=is%3Aissue+is%3Aopen) for the feature you are suggestion** in case it has already been suggested.
**Please use [Discord](https://discord.gg/RgpFBX3Q3k) or [GitHub Discussions](https://github.com/SideStore/SideStore/discussions) for support.**
- type: textarea
id: description
attributes:
label: Describe the feature
description: What is the feature? How would it work?
placeholder: Please be clear and concise with your description.
validations:
required: true
- type: textarea
id: use-cases
attributes:
label: Use cases
description: Please include multiple use cases where this feature would be useful.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: If you have alternative ideas of how this feature could work, you can put them here.

15
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,15 @@
### Changes
<!-- Fill this list with what your PR changes. Example: -->
- Fix bug
- Change UI for QOL
<!-- If your PR is ready to be merged, you can remove this section. -->
### Todo before merge
<!-- Example: -->
- [x] Finish UI changes
- [ ] Test
<!-- If your PR doesn't close an issue, you can remove the next line. -->
Closes #1234

View File

@@ -0,0 +1,22 @@
name: Add artifact links to pull request and related issues
on:
workflow_run:
workflows: [Pull Request SideStore build]
types: [completed]
jobs:
artifacts-url-comments:
name: add artifact links to pull request and related issues job
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: add artifact links to pull request and related issues step
uses: tonyhallett/artifacts-url-comments@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
prefix: Builds for this Pull Request are available at
suffix: Have a nice day.
format: name
addTo: pull
# addTo: pullandissues

123
.github/workflows/beta.yml vendored Normal file
View File

@@ -0,0 +1,123 @@
name: Beta SideStore build
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+' # example: 1.0.0-beta.1
jobs:
build:
name: Build and upload SideStore Beta
strategy:
fail-fast: false
matrix:
include:
- os: 'macos-12'
version: '14.2'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: recursive
- name: Install dependencies
run: brew install ldid
- name: Change version to tag
run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
- name: Change default icon to beta icon
run: sed -e 's/= Neon/= Starburst/' -i '' ./AltStore.xcodeproj/project.pbxproj
- name: Get version
id: version
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
- name: Echo version
run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1
with:
xcode-version: ${{ matrix.version }}
- name: "[Normal] Build SideStore, fakesign app and convert to IPA"
run: |
make build | xcpretty
make fakesign
make ipa
- name: Enable MDC
run: make enable_mdc
- name: "[MDC] Build SideStore, fakesign app and convert to IPA"
run: |
make clean
make build DSYM_FOLDER=./MDC-dSYM | xcpretty
make fakesign
make ipa IPA_NAME=SideStore-MDC.ipa
- name: Get current date
id: date
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
- name: Get current date in AltStore date form
id: date_altstore
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Upload to new beta release
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ steps.version.outputs.version }}
tag_name: ${{ github.ref_name }}
draft: true
prerelease: true
files: |
SideStore.ipa
SideStore-MDC.ipa
body: |
<!-- NOTE: to reset SideSource cache, go to `https://apps.sidestore.io/reset-cache/nightly/<sidesource key>`. This is not included in the GitHub Action since it makes draft releases so they can be edited and have a changelog. -->
Beta builds are hand-picked builds from development commits that will allow you to try out new features earlier than normal. However, **they might contain bugs and other issues. Use at your own risk!**
## Changelog
- TODO
## Build Info
Built at (UTC): `${{ steps.date.outputs.date }}`
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
Commit SHA: `${{ github.sha }}`
Version: `${{ steps.version.outputs.version }}`
- name: Add version to IPA file name
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Add version to MDC IPA file name
run: mv SideStore-MDC.ipa SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore-MDC.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
path: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./dSYM/*
- name: Upload MDC-dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}-dSYM
path: ./MDC-dSYM/*

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Ensure we are in root directory
cd "$(dirname "$0")/../.."
DATE=`date -u +'%Y.%m.%d'`
BUILD_NUM=1
write() {
sed -e "/MARKETING_VERSION = .*/s/$/-nightly.$DATE.$BUILD_NUM+$(git rev-parse --short HEAD)/" -i '' Build.xcconfig
echo "$DATE,$BUILD_NUM" > .nightly-build-num
}
if [ ! -f ".nightly-build-num" ]; then
write
exit 0
fi
LAST_DATE=`cat .nightly-build-num | perl -n -e '/([^,]*),([^ ]*)$/ && print $1'`
LAST_BUILD_NUM=`cat .nightly-build-num | perl -n -e '/([^,]*),([^ ]*)$/ && print $2'`
if [[ "$DATE" != "$LAST_DATE" ]]; then
write
else
BUILD_NUM=`expr $LAST_BUILD_NUM + 1`
write
fi

136
.github/workflows/nightly.yml vendored Normal file
View File

@@ -0,0 +1,136 @@
name: Nightly SideStore build
on:
push:
branches:
- develop
jobs:
build:
name: Build and upload SideStore Nightly
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
strategy:
fail-fast: false
matrix:
include:
- os: 'macos-12'
version: '14.2'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: recursive
- name: Install dependencies
run: brew install ldid
- name: Cache .nightly-build-num
uses: actions/cache@v3
with:
path: .nightly-build-num
key: nightly-build-num
- name: Increase nightly build number and set as version
run: bash .github/workflows/increase-nightly-build-num.sh
- name: Change default icon to nightly icon
run: sed -e 's/= Neon/= Steel/' -i '' ./AltStore.xcodeproj/project.pbxproj
- name: Enable unstable features
run: make enable_unstable
- name: Get version
id: version
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
- name: Echo version
run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1
with:
xcode-version: ${{ matrix.version }}
- name: "[Normal] Build SideStore, fakesign app and convert to IPA"
run: |
make build | xcpretty
make fakesign
make ipa
- name: Enable MDC
run: make enable_mdc
- name: "[MDC] Build SideStore, fakesign app and convert to IPA"
run: |
make clean
make build DSYM_FOLDER=./MDC-dSYM | xcpretty
make fakesign
make ipa IPA_NAME=SideStore-MDC.ipa
- name: Get current date
id: date
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
- name: Get current date in AltStore date form
id: date_altstore
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Upload to nightly release
uses: IsaacShelton/update-existing-release@v1.3.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
release: "Nightly"
tag: "nightly"
prerelease: true
files: |
SideStore.ipa
SideStore-MDC.ipa
body: |
This is an ⚠️ **EXPERIMENTAL** ⚠️ nightly build for commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}).
Nightly builds are **extremely experimental builds only meant to be used by developers and alpha testers. They often contain bugs and experimental features. Use at your own risk!**
If you want to try out new features early but want a lower chance of bugs, you can look at [SideStore Beta](https://github.com/${{ github.repository }}/releases?q=beta).
## Build Info
Built at (UTC): `${{ steps.date.outputs.date }}`
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
Commit SHA: `${{ github.sha }}`
Version: `${{ steps.version.outputs.version }}`
- name: Add version to IPA file name
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Add version to MDC IPA file name
run: mv SideStore-MDC.ipa SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore-MDC.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
path: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./dSYM/*
- name: Upload MDC-dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}-dSYM
path: ./MDC-dSYM/*
- name: Reset cache for apps.sidestore.io/nightly
run: sleep 10 && curl https://apps.sidestore.io/reset-cache/nightly/${{ secrets.SIDESOURCE_KEY }}

92
.github/workflows/pr.yml vendored Normal file
View File

@@ -0,0 +1,92 @@
name: Pull Request SideStore build
on:
pull_request:
jobs:
build:
name: Build and upload SideStore
strategy:
fail-fast: false
matrix:
include:
- os: 'macos-12'
version: '14.2'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: recursive
- name: Install dependencies
run: brew install ldid
- name: Add PR suffix to version
run: sed -e "/MARKETING_VERSION = .*/s/\$/-pr.${{ github.event.pull_request.number }}+$(git rev-parse --short ${COMMIT:-HEAD})/" -i '' Build.xcconfig
env:
COMMIT: ${{ github.event.pull_request.head.sha }}
- name: Change default icon to alpha icon
run: sed -e 's/= Neon/= Storm/' -i '' ./AltStore.xcodeproj/project.pbxproj
- name: Enable unstable features
run: make enable_unstable
- name: Get version
id: version
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
- name: Echo version
run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1
with:
xcode-version: ${{ matrix.version }}
- name: "[Normal] Build SideStore, fakesign app and convert to IPA"
run: |
make build | xcpretty
make fakesign
make ipa
- name: Enable MDC
run: make enable_mdc
- name: "[MDC] Build SideStore, fakesign app and convert to IPA"
run: |
make clean
make build DSYM_FOLDER=./MDC-dSYM | xcpretty
make fakesign
make ipa IPA_NAME=SideStore-MDC.ipa
- name: Add version to IPA file name
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Add version to MDC IPA file name
run: mv SideStore-MDC.ipa SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore-MDC.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
path: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./dSYM/*
- name: Upload MDC-dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}-dSYM
path: ./MDC-dSYM/*

117
.github/workflows/stable.yml vendored Normal file
View File

@@ -0,0 +1,117 @@
name: Stable SideStore build
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+' # example: 1.0.0
jobs:
build:
name: Build and upload SideStore
strategy:
fail-fast: false
matrix:
include:
- os: 'macos-12'
version: '14.2'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: recursive
- name: Install dependencies
run: brew install ldid
- name: Change version to tag
run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
- name: Get version
id: version
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
- name: Echo version
run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1
with:
xcode-version: ${{ matrix.version }}
- name: "[Normal] Build SideStore, fakesign app and convert to IPA"
run: |
make build | xcpretty
make fakesign
make ipa
- name: Enable MDC
run: make enable_mdc
- name: "[MDC] Build SideStore, fakesign app and convert to IPA"
run: |
make clean
make build DSYM_FOLDER=./MDC-dSYM | xcpretty
make fakesign
make ipa IPA_NAME=SideStore-MDC.ipa
- name: Get current date
id: date
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
- name: Get current date in AltStore date form
id: date_altstore
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Upload to new stable release
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ steps.version.outputs.version }}
tag_name: ${{ github.ref_name }}
draft: true
files: |
SideStore.ipa
SideStore-MDC.ipa
body: |
<!-- NOTE: to reset SideSource cache, go to `https://apps.sidestore.io/reset-cache/nightly/<sidesource key>`. This is not included in the GitHub Action since it makes draft releases so they can be edited and have a changelog. -->
## Changelog
- TODO
## Build Info
Built at (UTC): `${{ steps.date.outputs.date }}`
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
Commit SHA: `${{ github.sha }}`
Version: `${{ steps.version.outputs.version }}`
- name: Add version to IPA file name
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Add version to MDC IPA file name
run: mv SideStore-MDC.ipa SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore-MDC.ipa Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
path: SideStore-MDC-${{ steps.version.outputs.version }}.ipa
- name: Upload dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./dSYM/*
- name: Upload MDC-dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-MDC-${{ steps.version.outputs.version }}-dSYM
path: ./MDC-dSYM/*

20
.gitignore vendored
View File

@@ -8,7 +8,7 @@
## Build generated
build/
DerivedData
archive.xcarchive
## Various settings
*.pbxuser
!default.pbxuser
@@ -27,4 +27,20 @@ xcuserdata
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.hmap
/newrelic_agent.log
/CodeSigning.xcconfig
/.vscode
## AppCode specific
.idea/
Payload/
SideStore*.ipa
*dSYM
Dependencies/.*-prebuilt-fetch-*
Dependencies/minimuxer/*
Dependencies/em_proxy/*
!Dependencies/**/.gitkeep
.nightly-build-num

11
.gitmodules vendored
View File

@@ -1,12 +1,9 @@
[submodule "Dependencies/Roxas"]
path = Dependencies/Roxas
url = https://github.com/rileytestut/Roxas.git
[submodule "Dependencies/AltSign"]
path = Dependencies/AltSign
url = https://github.com/rileytestut/AltSign.git
[submodule "Dependencies/libimobiledevice"]
path = Dependencies/libimobiledevice
url = https://github.com/rileytestut/libimobiledevice.git
url = https://github.com/libimobiledevice/libimobiledevice
[submodule "Dependencies/libusbmuxd"]
path = Dependencies/libusbmuxd
url = https://github.com/libimobiledevice/libusbmuxd.git
@@ -16,3 +13,9 @@
[submodule "Dependencies/MarkdownAttributedString"]
path = Dependencies/MarkdownAttributedString
url = https://github.com/chockenberry/MarkdownAttributedString.git
[submodule "Dependencies/libimobiledevice-glue"]
path = Dependencies/libimobiledevice-glue
url = https://github.com/libimobiledevice/libimobiledevice-glue
[submodule "Dependencies/libfragmentzip"]
path = Dependencies/libfragmentzip
url = https://github.com/SideStore/libfragmentzip.git

3
AltBackup.xcconfig Normal file
View File

@@ -0,0 +1,3 @@
#include "Build.xcconfig"
PRODUCT_BUNDLE_IDENTIFIER = $(PRODUCT_BUNDLE_IDENTIFIER).AltBackup

View File

@@ -4,7 +4,7 @@
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.rileytestut.AltStore</string>
<string>group.$(APP_GROUP_IDENTIFIER)</string>
</array>
</dict>
</plist>

View File

@@ -4,10 +4,11 @@
<dict>
<key>ALTAppGroups</key>
<array>
<string>group.com.rileytestut.AltStore</string>
<string>group.$(APP_GROUP_IDENTIFIER)</string>
<string>group.com.SideStore.SideStore</string>
</array>
<key>ALTBundleIdentifier</key>
<string>com.rileytestut.AltBackup</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>

View File

@@ -160,7 +160,7 @@ private extension ViewController
self.textLabel.text = String(format: NSLocalizedString("%@ is inactive.", comment: ""),
Bundle.main.appName ?? NSLocalizedString("App", comment: ""))
self.detailTextLabel.text = String(format: NSLocalizedString("Refresh %@ in AltStore to continue using it.", comment: ""),
self.detailTextLabel.text = String(format: NSLocalizedString("Refresh %@ in SideStore to continue using it.", comment: ""),
Bundle.main.appName ?? NSLocalizedString("this app", comment: ""))
self.detailTextLabel.isHidden = false

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>6XVY5G3U44.com.rileytestut.AltDaemon</string>
<string>$(DEVELOPMENT_TEAM).$(ORG_IDENTIFIER).AltDaemon</string>
<key>get-task-allow</key>
<true/>
<key>platform-application</key>

View File

@@ -77,7 +77,7 @@ extension XPCConnectionHandler: NSXPCListenerDelegate
guard
let codeSigningInfo = signingInfo as? [String: Any],
let bundleIdentifier = codeSigningInfo["identifier"] as? String,
bundleIdentifier.contains("com.rileytestut.AltStore")
bundleIdentifier.contains(Bundle.Info.appbundleIdentifier)
else { return false }
let connection = XPCConnection(newConnection)

View File

@@ -1,23 +0,0 @@
//
// ALTPluginService.h
// AltPlugin
//
// Created by Riley Testut on 11/14/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
#import <Foundation/Foundation.h>
@class ALTAnisetteData;
NS_ASSUME_NONNULL_BEGIN
@interface ALTPluginService : NSObject
@property (class, nonatomic, readonly) ALTPluginService *sharedService;
- (ALTAnisetteData *)requestAnisetteData;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,105 +0,0 @@
//
// ALTPluginService.m
// AltPlugin
//
// Created by Riley Testut on 11/14/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
#import "ALTPluginService.h"
#import <dlfcn.h>
#import "ALTAnisetteData.h"
@import AppKit;
@interface AKAppleIDSession : NSObject
- (id)appleIDHeadersForRequest:(id)arg1;
@end
@interface AKDevice
+ (AKDevice *)currentDevice;
- (NSString *)uniqueDeviceIdentifier;
- (nullable NSString *)serialNumber;
- (NSString *)serverFriendlyDescription;
@end
@interface ALTPluginService ()
@property (nonatomic, readonly) NSISO8601DateFormatter *dateFormatter;
@end
@implementation ALTPluginService
+ (instancetype)sharedService
{
static ALTPluginService *_service = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_service = [[self alloc] init];
});
return _service;
}
- (instancetype)init
{
self = [super init];
if (self)
{
_dateFormatter = [[NSISO8601DateFormatter alloc] init];
}
return self;
}
+ (void)initialize
{
[[ALTPluginService sharedService] start];
}
- (void)start
{
dlopen("/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit", RTLD_NOW);
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification:) name:@"com.rileytestut.AltServer.FetchAnisetteData" object:nil];
}
- (ALTAnisetteData *)requestAnisetteData
{
NSMutableURLRequest* req = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:@"https://developerservices2.apple.com/services/QH65B2/listTeams.action?clientId=XABBG36SBA"]];
[req setHTTPMethod:@"POST"];
AKAppleIDSession *session = [[NSClassFromString(@"AKAppleIDSession") alloc] initWithIdentifier:@"com.apple.gs.xcode.auth"];
NSDictionary *headers = [session appleIDHeadersForRequest:req];
AKDevice *device = [NSClassFromString(@"AKDevice") currentDevice];
NSDate *date = [self.dateFormatter dateFromString:headers[@"X-Apple-I-Client-Time"]];
ALTAnisetteData *anisetteData = [[NSClassFromString(@"ALTAnisetteData") alloc] initWithMachineID:headers[@"X-Apple-I-MD-M"]
oneTimePassword:headers[@"X-Apple-I-MD"]
localUserID:headers[@"X-Apple-I-MD-LU"]
routingInfo:[headers[@"X-Apple-I-MD-RINFO"] longLongValue]
deviceUniqueIdentifier:device.uniqueDeviceIdentifier
deviceSerialNumber:device.serialNumber ?: @"C02LKHBBFD57" // serialNumber can be nil, so provide valid fallback serial number.
deviceDescription:device.serverFriendlyDescription
date:date
locale:[NSLocale currentLocale]
timeZone:[NSTimeZone localTimeZone]];
return anisetteData;
}
- (void)receiveNotification:(NSNotification *)notification
{
NSString *requestUUID = notification.userInfo[@"requestUUID"];
ALTAnisetteData *anisetteData = [self requestAnisetteData];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:anisetteData requiringSecureCoding:YES error:nil];
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.rileytestut.AltServer.AnisetteDataResponse" object:nil userInfo:@{@"requestUUID": requestUUID, @"anisetteData": data} deliverImmediately:YES];
}
@end

View File

@@ -1,171 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Riley Testut. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>ALTPluginService</string>
<key>Supported10.14PluginCompatibilityUUIDs</key>
<array>
<string># UUIDs for versions from 10.12 to 99.99.99</string>
<string># For mail version 10.0 (3226) on OS X Version 10.12 (build 16A319)</string>
<string>36CCB8BB-2207-455E-89BC-B9D6E47ABB5B</string>
<string># For mail version 10.1 (3251) on OS X Version 10.12.1 (build 16B2553a)</string>
<string>9054AFD9-2607-489E-8E63-8B09A749BC61</string>
<string># For mail version 10.2 (3259) on OS X Version 10.12.2 (build 16D12b)</string>
<string>1CD3B36A-0E3B-4A26-8F7E-5BDF96AAC97E</string>
<string># For mail version 10.3 (3273) on OS X Version 10.12.4 (build 16G1036)</string>
<string>21560BD9-A3CC-482E-9B99-95B7BF61EDC1</string>
<string># For mail version 11.0 (3441.0.1) on OS X Version 10.13 (build 17A315i)</string>
<string>C86CD990-4660-4E36-8CDA-7454DEB2E199</string>
<string># For mail version 12.0 (3445.100.39) on OS X Version 10.14.1 (build 18B45d)</string>
<string>A4343FAF-AE18-40D0-8A16-DFAE481AF9C1</string>
<string># For mail version 13.0 (3594.4.2) on OS X Version 10.15 (build 19A558d)</string>
<string>6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053</string>
</array>
<key>Supported10.15PluginCompatibilityUUIDs</key>
<array>
<string># UUIDs for versions from 10.12 to 99.99.99</string>
<string># For mail version 10.0 (3226) on OS X Version 10.12 (build 16A319)</string>
<string>36CCB8BB-2207-455E-89BC-B9D6E47ABB5B</string>
<string># For mail version 10.1 (3251) on OS X Version 10.12.1 (build 16B2553a)</string>
<string>9054AFD9-2607-489E-8E63-8B09A749BC61</string>
<string># For mail version 10.2 (3259) on OS X Version 10.12.2 (build 16D12b)</string>
<string>1CD3B36A-0E3B-4A26-8F7E-5BDF96AAC97E</string>
<string># For mail version 10.3 (3273) on OS X Version 10.12.4 (build 16G1036)</string>
<string>21560BD9-A3CC-482E-9B99-95B7BF61EDC1</string>
<string># For mail version 11.0 (3441.0.1) on OS X Version 10.13 (build 17A315i)</string>
<string>C86CD990-4660-4E36-8CDA-7454DEB2E199</string>
<string># For mail version 12.0 (3445.100.39) on OS X Version 10.14.1 (build 18B45d)</string>
<string>A4343FAF-AE18-40D0-8A16-DFAE481AF9C1</string>
<string># For mail version 13.0 (3594.4.2) on OS X Version 10.15 (build 19A558d)</string>
<string>6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053</string>
</array>
<key>Supported11.0PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.10PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.1PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.2PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.3PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.4PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.5PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.6PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.7PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.8PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.9PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported12.0PluginCompatibilityUUIDs</key>
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
<string>224E7F96-2099-499C-A501-63FB68C79CD2</string>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
</array>
<key>Supported12.1PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
</array>
<key>Supported12.2PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
</array>
<key>Supported12.3PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported12.4PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported12.5PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported12.6PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported12.7PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported12.8PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported12.9PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
</array>
<key>Supported13.0PluginCompatibilityUUIDs</key>
<array>
<string>25288CEF-7D9B-49A8-BE6B-E41DA6277CF3</string>
<string>6FF8B077-81FA-45A4-BD57-17CDE79F13A5</string>
<string>A4B49485-0377-4FAB-8D8E-E3B8018CFC21</string>
<string>890E3F5B-9490-4828-8F3F-B6561E513FCC</string>
</array>
</dict>
</plist>

Binary file not shown.

View File

@@ -1,15 +0,0 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "ALTDeviceManager.h"
#import "ALTWiredConnection.h"
#import "ALTNotificationConnection.h"
#import "ALTDebugConnection.h"
// Shared
#import "ALTConstants.h"
#import "ALTConnection.h"
#import "AltXPCProtocol.h"
#import "NSError+ALTServerError.h"
#import "CFNotificationName+AltStore.h"

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@@ -1,156 +0,0 @@
//
// AnisetteDataManager.swift
// AltServer
//
// Created by Riley Testut on 11/16/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
private extension Bundle
{
struct ID
{
static let mail = "com.apple.mail"
static let altXPC = "com.rileytestut.AltXPC"
}
}
private extension ALTAnisetteData
{
func sanitize(byReplacingBundleID bundleID: String)
{
guard let range = self.deviceDescription.lowercased().range(of: "(" + bundleID.lowercased()) else { return }
var adjustedDescription = self.deviceDescription[..<range.lowerBound]
adjustedDescription += "(com.apple.dt.Xcode/3594.4.19)>"
self.deviceDescription = String(adjustedDescription)
}
}
class AnisetteDataManager: NSObject
{
static let shared = AnisetteDataManager()
private var anisetteDataCompletionHandlers: [String: (Result<ALTAnisetteData, Error>) -> Void] = [:]
private var anisetteDataTimers: [String: Timer] = [:]
private lazy var xpcConnection: NSXPCConnection = {
let connection = NSXPCConnection(serviceName: Bundle.ID.altXPC)
connection.remoteObjectInterface = NSXPCInterface(with: AltXPCProtocol.self)
connection.resume()
return connection
}()
private override init()
{
super.init()
DistributedNotificationCenter.default().addObserver(self, selector: #selector(AnisetteDataManager.handleAnisetteDataResponse(_:)), name: Notification.Name("com.rileytestut.AltServer.AnisetteDataResponse"), object: nil)
}
func requestAnisetteData(_ completion: @escaping (Result<ALTAnisetteData, Error>) -> Void)
{
if #available(macOS 10.15, *)
{
self.requestAnisetteDataFromXPCService { (result) in
do
{
let anisetteData = try result.get()
completion(.success(anisetteData))
}
catch CocoaError.xpcConnectionInterrupted
{
// SIP and/or AMFI are not disabled, so fall back to Mail plug-in.
self.requestAnisetteDataFromPlugin { (result) in
completion(result)
}
}
catch
{
completion(.failure(error))
}
}
}
else
{
self.requestAnisetteDataFromPlugin { (result) in
completion(result)
}
}
}
func isXPCAvailable(completion: @escaping (Bool) -> Void)
{
guard let proxy = self.xpcConnection.remoteObjectProxyWithErrorHandler({ (error) in
completion(false)
}) as? AltXPCProtocol else { return }
proxy.ping {
completion(true)
}
}
}
private extension AnisetteDataManager
{
@available(macOS 10.15, *)
func requestAnisetteDataFromXPCService(completion: @escaping (Result<ALTAnisetteData, Error>) -> Void)
{
guard let proxy = self.xpcConnection.remoteObjectProxyWithErrorHandler({ (error) in
print("Anisette XPC Error:", error)
completion(.failure(error))
}) as? AltXPCProtocol else { return }
proxy.requestAnisetteData { (anisetteData, error) in
anisetteData?.sanitize(byReplacingBundleID: Bundle.ID.altXPC)
completion(Result(anisetteData, error))
}
}
func requestAnisetteDataFromPlugin(completion: @escaping (Result<ALTAnisetteData, Error>) -> Void)
{
let requestUUID = UUID().uuidString
self.anisetteDataCompletionHandlers[requestUUID] = completion
let timer = Timer(timeInterval: 1.0, repeats: false) { (timer) in
self.finishRequest(forUUID: requestUUID, result: .failure(ALTServerError(.pluginNotFound)))
}
self.anisetteDataTimers[requestUUID] = timer
RunLoop.main.add(timer, forMode: .default)
DistributedNotificationCenter.default().postNotificationName(Notification.Name("com.rileytestut.AltServer.FetchAnisetteData"), object: nil, userInfo: ["requestUUID": requestUUID], options: .deliverImmediately)
}
@objc func handleAnisetteDataResponse(_ notification: Notification)
{
guard let userInfo = notification.userInfo, let requestUUID = userInfo["requestUUID"] as? String else { return }
if
let archivedAnisetteData = userInfo["anisetteData"] as? Data,
let anisetteData = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ALTAnisetteData.self, from: archivedAnisetteData)
{
anisetteData.sanitize(byReplacingBundleID: Bundle.ID.mail)
self.finishRequest(forUUID: requestUUID, result: .success(anisetteData))
}
else
{
self.finishRequest(forUUID: requestUUID, result: .failure(ALTServerError(.invalidAnisetteData)))
}
}
func finishRequest(forUUID requestUUID: String, result: Result<ALTAnisetteData, Error>)
{
let completionHandler = self.anisetteDataCompletionHandlers[requestUUID]
self.anisetteDataCompletionHandlers[requestUUID] = nil
let timer = self.anisetteDataTimers[requestUUID]
self.anisetteDataTimers[requestUUID] = nil
timer?.invalidate()
completionHandler?(result)
}
}

View File

@@ -1,610 +0,0 @@
//
// AppDelegate.swift
// AltServer
//
// Created by Riley Testut on 5/24/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Cocoa
import UserNotifications
import AltSign
import LaunchAtLogin
import Sparkle
#if STAGING
private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/altstore-staging/altstore.ipa")!
#elseif BETA
private let altstoreAppURL = URL(string: "https://cdn.altstore.io/file/altstore/altstore-beta.ipa")!
#else
private let altstoreAppURL = URL(string: "https://cdn.altstore.io/file/altstore/altstore.ipa")!
#endif
extension ALTDevice: MenuDisplayable {}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
private let pluginManager = PluginManager()
private var statusItem: NSStatusItem?
private var connectedDevices = [ALTDevice]()
private weak var authenticationAlert: NSAlert?
@IBOutlet private var appMenu: NSMenu!
@IBOutlet private var connectedDevicesMenu: NSMenu!
@IBOutlet private var sideloadIPAConnectedDevicesMenu: NSMenu!
@IBOutlet private var enableJITMenu: NSMenu!
@IBOutlet private var launchAtLoginMenuItem: NSMenuItem!
@IBOutlet private var installMailPluginMenuItem: NSMenuItem!
@IBOutlet private var installAltStoreMenuItem: NSMenuItem!
@IBOutlet private var sideloadAppMenuItem: NSMenuItem!
private weak var authenticationAppleIDTextField: NSTextField?
private weak var authenticationPasswordTextField: NSSecureTextField?
private var connectedDevicesMenuController: MenuController<ALTDevice>!
private var sideloadIPAConnectedDevicesMenuController: MenuController<ALTDevice>!
private var enableJITMenuController: MenuController<ALTDevice>!
private var _jitAppListMenuControllers = [AnyObject]()
private var isAltPluginUpdateAvailable = false
func applicationDidFinishLaunching(_ aNotification: Notification)
{
UserDefaults.standard.registerDefaults()
UNUserNotificationCenter.current().delegate = self
ServerConnectionManager.shared.start()
ALTDeviceManager.shared.start()
#if STAGING
SUUpdater.shared().feedURL = URL(string: "https://altstore.io/altserver/sparkle-macos-staging.xml")
#else
SUUpdater.shared().feedURL = URL(string: "https://altstore.io/altserver/sparkle-macos.xml")
#endif
let item = NSStatusBar.system.statusItem(withLength: -1)
item.menu = self.appMenu
item.button?.image = NSImage(named: "MenuBarIcon")
self.statusItem = item
self.appMenu.delegate = self
self.sideloadAppMenuItem.keyEquivalentModifierMask = .option
self.sideloadAppMenuItem.isAlternate = true
let placeholder = NSLocalizedString("No Connected Devices", comment: "")
self.connectedDevicesMenuController = MenuController<ALTDevice>(menu: self.connectedDevicesMenu, items: [])
self.connectedDevicesMenuController.placeholder = placeholder
self.connectedDevicesMenuController.action = { [weak self] device in
self?.installAltStore(to: device)
}
self.sideloadIPAConnectedDevicesMenuController = MenuController<ALTDevice>(menu: self.sideloadIPAConnectedDevicesMenu, items: [])
self.sideloadIPAConnectedDevicesMenuController.placeholder = placeholder
self.sideloadIPAConnectedDevicesMenuController.action = { [weak self] device in
self?.sideloadIPA(to: device)
}
self.enableJITMenuController = MenuController<ALTDevice>(menu: self.enableJITMenu, items: [])
self.enableJITMenuController.placeholder = placeholder
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
guard success else { return }
if !UserDefaults.standard.didPresentInitialNotification
{
let content = UNMutableNotificationContent()
content.title = NSLocalizedString("AltServer Running", comment: "")
content.body = NSLocalizedString("AltServer runs in the background as a menu bar app listening for AltStore.", comment: "")
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
UserDefaults.standard.didPresentInitialNotification = true
}
}
self.pluginManager.isUpdateAvailable { result in
guard let isUpdateAvailable = try? result.get() else { return }
self.isAltPluginUpdateAvailable = isUpdateAvailable
if isUpdateAvailable
{
self.installMailPlugin()
}
}
}
func applicationWillTerminate(_ aNotification: Notification)
{
// Insert code here to tear down your application
}
}
private extension AppDelegate
{
@objc func installAltStore(to device: ALTDevice)
{
self.installApplication(at: altstoreAppURL, to: device)
}
@objc func sideloadIPA(to device: ALTDevice)
{
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
let openPanel = NSOpenPanel()
openPanel.canChooseDirectories = false
openPanel.allowsMultipleSelection = false
openPanel.allowedFileTypes = ["ipa"]
openPanel.begin { (response) in
guard let fileURL = openPanel.url, response == .OK else { return }
self.installApplication(at: fileURL, to: device)
}
}
func enableJIT(for app: InstalledApp, on device: ALTDevice)
{
func finish(_ result: Result<Void, Error>)
{
DispatchQueue.main.async {
switch result
{
case .failure(let error):
self.showErrorAlert(error: error, localizedFailure: String(format: NSLocalizedString("JIT compilation could not be enabled for %@.", comment: ""), app.name))
case .success:
let alert = NSAlert()
alert.messageText = String(format: NSLocalizedString("Successfully enabled JIT for %@.", comment: ""), app.name)
alert.informativeText = String(format: NSLocalizedString("JIT will remain enabled until you quit the app. You can now disconnect %@ from your computer.", comment: ""), device.name)
alert.runModal()
}
}
}
ALTDeviceManager.shared.prepare(device) { (result) in
switch result
{
case .failure(let error as NSError): return finish(.failure(error))
case .success:
ALTDeviceManager.shared.startDebugConnection(to: device) { (connection, error) in
guard let connection = connection else {
return finish(.failure(error! as NSError))
}
connection.enableUnsignedCodeExecutionForProcess(withName: app.executableName) { (success, error) in
guard success else {
return finish(.failure(error!))
}
finish(.success(()))
}
}
}
}
}
func installApplication(at url: URL, to device: ALTDevice)
{
let alert = NSAlert()
alert.messageText = NSLocalizedString("Please enter your Apple ID and password.", comment: "")
alert.informativeText = NSLocalizedString("Your Apple ID and password are not saved and are only sent to Apple for authentication.", comment: "")
let textFieldSize = NSSize(width: 300, height: 22)
let appleIDTextField = NSTextField(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height))
appleIDTextField.delegate = self
appleIDTextField.translatesAutoresizingMaskIntoConstraints = false
appleIDTextField.placeholderString = NSLocalizedString("Apple ID", comment: "")
alert.window.initialFirstResponder = appleIDTextField
self.authenticationAppleIDTextField = appleIDTextField
let passwordTextField = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height))
passwordTextField.delegate = self
passwordTextField.translatesAutoresizingMaskIntoConstraints = false
passwordTextField.placeholderString = NSLocalizedString("Password", comment: "")
self.authenticationPasswordTextField = passwordTextField
appleIDTextField.nextKeyView = passwordTextField
let stackView = NSStackView(frame: NSRect(x: 0, y: 0, width: textFieldSize.width, height: textFieldSize.height * 2))
stackView.orientation = .vertical
stackView.distribution = .equalSpacing
stackView.spacing = 0
stackView.addArrangedSubview(appleIDTextField)
stackView.addArrangedSubview(passwordTextField)
alert.accessoryView = stackView
alert.addButton(withTitle: NSLocalizedString("Install", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
self.authenticationAlert = alert
self.validate()
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
let response = alert.runModal()
guard response == .alertFirstButtonReturn else { return }
let username = appleIDTextField.stringValue
let password = passwordTextField.stringValue
func finish(_ result: Result<ALTApplication, Error>)
{
switch result
{
case .success(let application):
let content = UNMutableNotificationContent()
content.title = NSLocalizedString("Installation Succeeded", comment: "")
content.body = String(format: NSLocalizedString("%@ was successfully installed on %@.", comment: ""), application.name, device.name)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
case .failure(InstallError.cancelled), .failure(ALTAppleAPIError.requiresTwoFactorAuthentication):
// Ignore
break
case .failure(let error):
DispatchQueue.main.async {
self.showErrorAlert(error: error, localizedFailure: String(format: NSLocalizedString("Could not install app to %@.", comment: ""), device.name))
}
}
}
func install()
{
ALTDeviceManager.shared.installApplication(at: url, to: device, appleID: username, password: password, completion: finish(_:))
}
AnisetteDataManager.shared.isXPCAvailable { isAvailable in
if isAvailable
{
// XPC service is available, so we don't need to install/update Mail plug-in.
// Users can still manually do so from the AltServer menu.
install()
}
else
{
self.pluginManager.isUpdateAvailable { result in
switch result
{
case .failure(let error): finish(.failure(error))
case .success(let isUpdateAvailable):
self.isAltPluginUpdateAvailable = isUpdateAvailable
if !self.pluginManager.isMailPluginInstalled || isUpdateAvailable
{
self.installMailPlugin { result in
switch result
{
case .failure: break
case .success: install()
}
}
}
else
{
install()
}
}
}
}
}
}
func showErrorAlert(error: Error, localizedFailure: String)
{
let nsError = error as NSError
let alert = NSAlert()
alert.alertStyle = .critical
alert.messageText = localizedFailure
var messageComponents = [String]()
let separator: String
switch error
{
case ALTServerError.maximumFreeAppLimitReached: separator = "\n\n"
default: separator = " "
}
if let errorFailure = nsError.localizedFailure
{
if let debugDescription = nsError.localizedDebugDescription
{
alert.messageText = errorFailure
messageComponents.append(debugDescription)
}
else if let failureReason = nsError.localizedFailureReason
{
if nsError.localizedDescription.starts(with: errorFailure)
{
alert.messageText = errorFailure
messageComponents.append(failureReason)
}
else
{
alert.messageText = errorFailure
messageComponents.append(nsError.localizedDescription)
}
}
else
{
// No failure reason given.
if nsError.localizedDescription.starts(with: errorFailure)
{
// No need to duplicate errorFailure in both title and message.
alert.messageText = localizedFailure
messageComponents.append(nsError.localizedDescription)
}
else
{
alert.messageText = errorFailure
messageComponents.append(nsError.localizedDescription)
}
}
}
else
{
alert.messageText = localizedFailure
if let debugDescription = nsError.localizedDebugDescription
{
messageComponents.append(debugDescription)
}
else
{
messageComponents.append(nsError.localizedDescription)
}
}
if let recoverySuggestion = nsError.localizedRecoverySuggestion
{
messageComponents.append(recoverySuggestion)
}
let informativeText = messageComponents.joined(separator: separator)
alert.informativeText = informativeText
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
alert.runModal()
}
@objc func toggleLaunchAtLogin(_ item: NSMenuItem)
{
LaunchAtLogin.isEnabled.toggle()
}
@objc func handleInstallMailPluginMenuItem(_ item: NSMenuItem)
{
if !self.pluginManager.isMailPluginInstalled || self.isAltPluginUpdateAvailable
{
self.installMailPlugin()
}
else
{
self.uninstallMailPlugin()
}
}
private func installMailPlugin(completion: ((Result<Void, Error>) -> Void)? = nil)
{
self.pluginManager.installMailPlugin { (result) in
DispatchQueue.main.async {
switch result
{
case .failure(PluginError.cancelled): break
case .failure(let error):
let alert = NSAlert()
alert.messageText = NSLocalizedString("Failed to Install Mail Plug-in", comment: "")
alert.informativeText = error.localizedDescription
alert.runModal()
case .success:
let alert = NSAlert()
alert.messageText = NSLocalizedString("Mail Plug-in Installed", comment: "")
alert.informativeText = NSLocalizedString("Please restart Mail and enable AltPlugin in Mail's Preferences. Mail must be running when installing or refreshing apps with AltServer.", comment: "")
alert.runModal()
self.isAltPluginUpdateAvailable = false
}
completion?(result)
}
}
}
private func uninstallMailPlugin()
{
self.pluginManager.uninstallMailPlugin { (result) in
DispatchQueue.main.async {
switch result
{
case .failure(PluginError.cancelled): break
case .failure(let error):
let alert = NSAlert()
alert.messageText = NSLocalizedString("Failed to Uninstall Mail Plug-in", comment: "")
alert.informativeText = error.localizedDescription
alert.runModal()
case .success:
let alert = NSAlert()
alert.messageText = NSLocalizedString("Mail Plug-in Uninstalled", comment: "")
alert.informativeText = NSLocalizedString("Please restart Mail for changes to take effect. You will not be able to use AltServer until the plug-in is reinstalled.", comment: "")
alert.runModal()
}
}
}
}
}
extension AppDelegate: NSMenuDelegate
{
func menuWillOpen(_ menu: NSMenu)
{
guard menu == self.appMenu else { return }
// Clear any cached _jitAppListMenuControllers.
self._jitAppListMenuControllers.removeAll()
self.connectedDevices = ALTDeviceManager.shared.availableDevices
self.connectedDevicesMenuController.items = self.connectedDevices
self.sideloadIPAConnectedDevicesMenuController.items = self.connectedDevices
self.enableJITMenuController.items = self.connectedDevices
self.launchAtLoginMenuItem.target = self
self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:))
self.launchAtLoginMenuItem.state = LaunchAtLogin.isEnabled ? .on : .off
if self.isAltPluginUpdateAvailable
{
self.installMailPluginMenuItem.title = NSLocalizedString("Update Mail Plug-in…", comment: "")
}
else if self.pluginManager.isMailPluginInstalled
{
self.installMailPluginMenuItem.title = NSLocalizedString("Uninstall Mail Plug-in…", comment: "")
}
else
{
self.installMailPluginMenuItem.title = NSLocalizedString("Install Mail Plug-in…", comment: "")
}
self.installMailPluginMenuItem.target = self
self.installMailPluginMenuItem.action = #selector(AppDelegate.handleInstallMailPluginMenuItem(_:))
// Need to re-set this every time menu appears so we can refresh device app list.
self.enableJITMenuController.submenuHandler = { [weak self] device in
let submenu = NSMenu(title: NSLocalizedString("Sideloaded Apps", comment: ""))
guard let `self` = self else { return submenu }
let submenuController = MenuController<InstalledApp>(menu: submenu, items: [])
submenuController.placeholder = NSLocalizedString("Loading...", comment: "")
submenuController.action = { [weak self] (appInfo) in
self?.enableJIT(for: appInfo, on: device)
}
// Keep strong reference
self._jitAppListMenuControllers.append(submenuController)
ALTDeviceManager.shared.fetchInstalledApps(on: device) { (installedApps, error) in
DispatchQueue.main.async {
guard let installedApps = installedApps else {
print("Failed to fetch installed apps from \(device).", error!)
submenuController.placeholder = error?.localizedDescription
return
}
print("Fetched \(installedApps.count) apps for \(device).")
let sortedApps = installedApps.sorted { (app1, app2) in
if app1.name == app2.name
{
return app1.bundleIdentifier < app2.bundleIdentifier
}
else
{
return app1.name < app2.name
}
}
submenuController.items = sortedApps
if submenuController.items.isEmpty
{
submenuController.placeholder = NSLocalizedString("No Sideloaded Apps", comment: "")
}
}
}
return submenu
}
}
func menuDidClose(_ menu: NSMenu)
{
guard menu == self.appMenu else { return }
// Clearing _jitAppListMenuControllers now prevents action handler from being called.
// self._jitAppListMenuControllers = []
// Set `submenuHandler` to nil to prevent prematurely fetching installed apps in menuWillOpen(_:)
// when assigning self.connectedDevices to `items` (which implicitly calls `submenuHandler`)
self.enableJITMenuController.submenuHandler = nil
}
func menu(_ menu: NSMenu, willHighlight item: NSMenuItem?)
{
guard menu == self.appMenu else { return }
// The submenu won't update correctly if the user holds/releases
// the Option key while the submenu is visible.
// Workaround: temporarily set submenu to nil to dismiss it,
// which will then cause the correct submenu to appear.
let previousItem: NSMenuItem
switch item
{
case self.sideloadAppMenuItem: previousItem = self.installAltStoreMenuItem
case self.installAltStoreMenuItem: previousItem = self.sideloadAppMenuItem
default: return
}
let submenu = previousItem.submenu
previousItem.submenu = nil
previousItem.submenu = submenu
}
}
extension AppDelegate: NSTextFieldDelegate
{
func controlTextDidChange(_ obj: Notification)
{
self.validate()
}
func controlTextDidEndEditing(_ obj: Notification)
{
self.validate()
}
private func validate()
{
guard
let appleID = self.authenticationAppleIDTextField?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines),
let password = self.authenticationPasswordTextField?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
else { return }
if appleID.isEmpty || password.isEmpty
{
self.authenticationAlert?.buttons.first?.isEnabled = false
}
else
{
self.authenticationAlert?.buttons.first?.isEnabled = true
}
self.authenticationAlert?.layout()
}
}
extension AppDelegate: UNUserNotificationCenterDelegate
{
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler([.alert, .sound, .badge])
}
}

View File

@@ -1,68 +0,0 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "Icon@16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "Icon@32-1.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "Icon@32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "Icon@64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "Icon@128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "Icon@256-1.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "Icon@256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "Icon@512-1.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "Icon@512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "Icon@1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -1,25 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "MenuBar@19.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "MenuBar@38.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,388 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Application-->
<scene sceneID="JPo-4y-FX3">
<objects>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="4" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" id="urc-xw-Dhc">
<rect key="frame" x="0.0" y="0.0" width="300" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zLd-d8-ghZ">
<rect key="frame" x="0.0" y="25" width="300" height="21"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Apple ID" drawsBackground="YES" id="BXa-Re-rs3">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="QtW-r2-Vuh"/>
<outlet property="nextKeyView" destination="9rp-Vx-rvB" id="bQY-qj-Sej"/>
</connections>
</textField>
<secureTextField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9rp-Vx-rvB">
<rect key="frame" x="0.0" y="0.0" width="300" height="21"/>
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Password" drawsBackground="YES" usesSingleLineMode="YES" id="xqJ-wt-DlP">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</secureTextFieldCell>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="qav-xj-izy"/>
</connections>
</secureTextField>
</subviews>
<constraints>
<constraint firstItem="9rp-Vx-rvB" firstAttribute="width" secondItem="urc-xw-Dhc" secondAttribute="width" id="Eht-pU-Gyh"/>
<constraint firstItem="zLd-d8-ghZ" firstAttribute="width" secondItem="urc-xw-Dhc" secondAttribute="width" id="mg7-Kq-abL"/>
<constraint firstAttribute="width" constant="300" id="zqf-x6-BET"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="AltServer" customModuleProvider="target">
<connections>
<outlet property="appMenu" destination="uQy-DD-JDr" id="7cY-Ov-AOW"/>
<outlet property="authenticationAppleIDTextField" destination="zLd-d8-ghZ" id="wW5-0J-zdq"/>
<outlet property="authenticationPasswordTextField" destination="9rp-Vx-rvB" id="ZoC-DI-jzQ"/>
<outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/>
<outlet property="enableJITMenu" destination="la4-Sa-L3C" id="YrW-hR-uA7"/>
<outlet property="installAltStoreMenuItem" destination="MJ8-Lt-SSV" id="KYp-c5-8Ru"/>
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
<outlet property="sideloadAppMenuItem" destination="x0e-zI-0A2" id="GJo-FY-1GO"/>
<outlet property="sideloadIPAConnectedDevicesMenu" destination="IuI-bV-fTY" id="QQw-St-HfG"/>
</connections>
</customObject>
<customObject id="Arf-IC-5eb" customClass="SUUpdater"/>
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="AltServer" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="AltServer" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About AltServer" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Install AltStore…" id="MJ8-Lt-SSV">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Install AltStore…" systemMenu="recentDocuments" id="KJ9-WY-pW1">
<items>
<menuItem title="No Connected Devices" id="N5N-3K-XuR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="DKG-yI-Ujv"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Sideload .ipa…" id="x0e-zI-0A2" userLabel="Install .ipa">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Sideload .ipa…" systemMenu="recentDocuments" id="IuI-bV-fTY">
<items>
<menuItem title="No Connected Devices" id="in5-an-MD0">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="aUE-On-axK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Enable JIT" id="TvL-8M-oPZ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Enable JIT" systemMenu="recentDocuments" id="la4-Sa-L3C">
<items>
<menuItem title="No Connected Devices" id="Aya-FP-EzE">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="1ZZ-BB-xHy"/>
<menuItem title="Launch at Login" id="IyR-FQ-upe" userLabel="Launch At Login">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Install Mail Plug-in…" id="3CM-gV-X2G" userLabel="Mail Plug-in">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="mVM-Nm-Zi9"/>
<menuItem title="Check for Updates..." id="Tnq-gD-Eic" userLabel="Check for Updates">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="checkForUpdates:" target="Arf-IC-5eb" id="7JG-du-nr4"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="hmG-xg-qgm"/>
<menuItem title="Quit AltServer" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="AltServer Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
</objects>
<point key="canvasLocation" x="75" y="0.0"/>
</scene>
</scenes>
</document>

View File

@@ -1,25 +0,0 @@
//
// NSError+libimobiledevice.h
// AltServer
//
// Created by Riley Testut on 3/23/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
#import <libimobiledevice/mobile_image_mounter.h>
#import <libimobiledevice/debugserver.h>
#import <libimobiledevice/installation_proxy.h>
#import <AltSign/ALTDevice.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSError (libimobiledevice)
+ (nullable instancetype)errorWithMobileImageMounterError:(mobile_image_mounter_error_t)error device:(nullable ALTDevice *)device;
+ (nullable instancetype)errorWithDebugServerError:(debugserver_error_t)error device:(nullable ALTDevice *)device;
+ (nullable instancetype)errorWithInstallationProxyError:(instproxy_error_t)error device:(nullable ALTDevice *)device;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,86 +0,0 @@
//
// NSError+libimobiledevice.m
// AltServer
//
// Created by Riley Testut on 3/23/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
#import "NSError+libimobiledevice.h"
#import "NSError+ALTServerError.h"
@implementation NSError (libimobiledevice)
+ (nullable instancetype)errorWithMobileImageMounterError:(mobile_image_mounter_error_t)error device:(nullable ALTDevice *)device
{
NSMutableDictionary *userInfo = [@{
ALTUnderlyingErrorDomainErrorKey: @"Mobile Image Mounter",
ALTUnderlyingErrorCodeErrorKey: [@(error) description],
} mutableCopy];
if (device)
{
userInfo[ALTDeviceNameErrorKey] = device.name;
}
switch (error)
{
case MOBILE_IMAGE_MOUNTER_E_SUCCESS: return nil;
case MOBILE_IMAGE_MOUNTER_E_INVALID_ARG: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo];
case MOBILE_IMAGE_MOUNTER_E_PLIST_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidResponse userInfo:userInfo];
case MOBILE_IMAGE_MOUNTER_E_CONN_FAILED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUsbmuxd userInfo:userInfo];
case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo];
case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorDeviceLocked userInfo:userInfo];
case MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:userInfo];
}
}
+ (nullable instancetype)errorWithDebugServerError:(debugserver_error_t)error device:(nullable ALTDevice *)device
{
NSMutableDictionary *userInfo = [@{
ALTUnderlyingErrorDomainErrorKey: @"Debug Server",
ALTUnderlyingErrorCodeErrorKey: [@(error) description],
} mutableCopy];
if (device)
{
userInfo[ALTDeviceNameErrorKey] = device.name;
}
switch (error)
{
case DEBUGSERVER_E_SUCCESS: return nil;
case DEBUGSERVER_E_INVALID_ARG: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo];
case DEBUGSERVER_E_MUX_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUsbmuxd userInfo:userInfo];
case DEBUGSERVER_E_SSL_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorSSL userInfo:userInfo];
case DEBUGSERVER_E_RESPONSE_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidResponse userInfo:userInfo];
case DEBUGSERVER_E_TIMEOUT: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorTimedOut userInfo:userInfo];
case DEBUGSERVER_E_UNKNOWN_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:userInfo];
}
}
+ (nullable instancetype)errorWithInstallationProxyError:(instproxy_error_t)error device:(nullable ALTDevice *)device
{
NSMutableDictionary *userInfo = [@{
ALTUnderlyingErrorDomainErrorKey: @"Installation Proxy",
ALTUnderlyingErrorCodeErrorKey: [@(error) description],
} mutableCopy];
if (device)
{
userInfo[ALTDeviceNameErrorKey] = device.name;
}
switch (error)
{
case INSTPROXY_E_SUCCESS: return nil;
case INSTPROXY_E_INVALID_ARG: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidRequest userInfo:userInfo];
case INSTPROXY_E_PLIST_ERROR: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorInvalidResponse userInfo:userInfo];
case INSTPROXY_E_CONN_FAILED: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUsbmuxd userInfo:userInfo];
case INSTPROXY_E_RECEIVE_TIMEOUT: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorTimedOut userInfo:userInfo];
// case INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW: return [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorUnsupportediOSVersion userInfo:nil]; // Error message assumes we're installing AltStore
default: return [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:userInfo];
}
}
@end

View File

@@ -1,28 +0,0 @@
//
// ALTDebugConnection+Private.h
// AltServer
//
// Created by Riley Testut on 2/19/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
#import "ALTDebugConnection.h"
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/debugserver.h>
NS_ASSUME_NONNULL_BEGIN
@interface ALTDebugConnection ()
@property (nonatomic, readonly) dispatch_queue_t connectionQueue;
@property (nonatomic, nullable) debugserver_client_t client;
- (instancetype)initWithDevice:(ALTDevice *)device;
- (void)connectWithCompletionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,25 +0,0 @@
//
// ALTDebugConnection.h
// AltServer
//
// Created by Riley Testut on 2/19/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
#import "AltSign.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(DebugConnection)
@interface ALTDebugConnection : NSObject
@property (nonatomic, copy, readonly) ALTDevice *device;
- (void)enableUnsignedCodeExecutionForProcessWithName:(NSString *)processName completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)enableUnsignedCodeExecutionForProcessWithID:(NSInteger)pid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)disconnect;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,315 +0,0 @@
//
// ALTDebugConnection.m
// AltServer
//
// Created by Riley Testut on 2/19/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
#import "ALTDebugConnection+Private.h"
#import "NSError+ALTServerError.h"
#import "NSError+libimobiledevice.h"
char *bin2hex(const unsigned char *bin, size_t length)
{
if (bin == NULL || length == 0)
{
return NULL;
}
char *hex = (char *)malloc(length * 2 + 1);
for (size_t i = 0; i < length; i++)
{
hex[i * 2] = "0123456789ABCDEF"[bin[i] >> 4];
hex[i * 2 + 1] = "0123456789ABCDEF"[bin[i] & 0x0F];
}
hex[length * 2] = '\0';
return hex;
}
@implementation ALTDebugConnection
- (instancetype)initWithDevice:(ALTDevice *)device
{
self = [super init];
if (self)
{
_device = device;
_connectionQueue = dispatch_queue_create_with_target("io.altstore.AltServer.DebugConnection",
DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL,
dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0));
}
return self;
}
- (void)dealloc
{
[self disconnect];
}
- (void)disconnect
{
if (_client == nil)
{
return;
}
debugserver_client_free(_client);
_client = nil;
}
- (void)connectWithCompletionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler
{
__block idevice_t device = NULL;
void (^finish)(BOOL, NSError *) = ^(BOOL success, NSError *error) {
if (device)
{
idevice_free(device);
}
completionHandler(success, error);
};
dispatch_async(self.connectionQueue, ^{
/* Find Device */
if (idevice_new_with_options(&device, self.device.identifier.UTF8String, (enum idevice_options)((int)IDEVICE_LOOKUP_NETWORK | (int)IDEVICE_LOOKUP_USBMUX)) != IDEVICE_E_SUCCESS)
{
return finish(NO, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]);
}
/* Connect to debugserver */
debugserver_client_t client = NULL;
debugserver_error_t error = debugserver_client_start_service(device, &client, "AltServer");
if (error != DEBUGSERVER_E_SUCCESS)
{
return finish(NO, [NSError errorWithDebugServerError:error device:self.device]);
}
self.client = client;
finish(YES, nil);
});
}
- (void)enableUnsignedCodeExecutionForProcessWithName:(NSString *)processName completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler
{
[self _enableUnsignedCodeExecutionForProcessWithName:processName pid:0 completionHandler:completionHandler];
}
- (void)enableUnsignedCodeExecutionForProcessWithID:(NSInteger)pid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler
{
[self _enableUnsignedCodeExecutionForProcessWithName:nil pid:(int32_t)pid completionHandler:completionHandler];
}
- (void)_enableUnsignedCodeExecutionForProcessWithName:(nullable NSString *)processName pid:(int32_t)pid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler
{
dispatch_async(self.connectionQueue, ^{
NSString *name = processName ?: NSLocalizedString(@"this app", @"");
NSString *localizedFailure = [NSString stringWithFormat:NSLocalizedString(@"JIT could not be enabled for %@.", comment: @""), name];
NSString *attachCommand = nil;
if (processName)
{
NSString *encodedName = @(bin2hex((const unsigned char *)processName.UTF8String, (size_t)strlen(processName.UTF8String)));
attachCommand = [NSString stringWithFormat:@"vAttachOrWait;%@", encodedName];
}
else
{
// Convert to Big-endian.
int32_t rawPID = CFSwapInt32HostToBig(pid);
NSString *encodedName = @(bin2hex((const unsigned char *)&rawPID, 4));
attachCommand = [NSString stringWithFormat:@"vAttach;%@", encodedName];
}
NSError *error = nil;
if (![self sendCommand:attachCommand arguments:nil error:&error])
{
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
userInfo[ALTAppNameErrorKey] = processName;
userInfo[ALTDeviceNameErrorKey] = self.device.name;
userInfo[NSLocalizedFailureErrorKey] = localizedFailure;
NSError *returnError = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
return completionHandler(NO, returnError);
}
NSString *detachCommand = @"D";
if (![self sendCommand:detachCommand arguments:nil error:&error])
{
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
userInfo[ALTAppNameErrorKey] = processName;
userInfo[ALTDeviceNameErrorKey] = self.device.name;
userInfo[NSLocalizedFailureErrorKey] = localizedFailure;
NSError *returnError = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
return completionHandler(NO, returnError);
}
completionHandler(YES, nil);
});
}
#pragma mark - Private -
- (BOOL)sendCommand:(NSString *)command arguments:(nullable NSArray<NSString *> *)arguments error:(NSError **)error
{
int argc = (int)arguments.count;
char **argv = new char*[argc + 1];
for (int i = 0; i < argc; i++)
{
NSString *argument = arguments[i];
argv[i] = (char *)argument.UTF8String;
}
argv[argc] = NULL;
debugserver_command_t debugCommand = NULL;
debugserver_command_new(command.UTF8String, argc, argv, &debugCommand);
delete[] argv;
char *response = NULL;
size_t responseSize = 0;
debugserver_error_t debugServerError = debugserver_client_send_command(self.client, debugCommand, &response, &responseSize);
debugserver_command_free(debugCommand);
if (debugServerError != DEBUGSERVER_E_SUCCESS)
{
if (error)
{
*error = [NSError errorWithDebugServerError:debugServerError device:self.device];
}
return NO;
}
NSString *rawResponse = (response != nil) ? @(response) : nil;
if (![self processResponse:rawResponse error:error])
{
return NO;
}
return YES;
}
- (BOOL)processResponse:(NSString *)rawResponse error:(NSError **)error
{
if (rawResponse == nil)
{
if (error)
{
*error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorRequestedAppNotRunning userInfo:nil];
return NO;
}
}
if (rawResponse.length == 0 || [rawResponse isEqualToString:@"OK"])
{
return YES;
}
char type = [rawResponse characterAtIndex:0];
NSString *response = [rawResponse substringFromIndex:1];
switch (type)
{
case 'O':
{
// stdout/stderr
char *decodedResponse = NULL;
debugserver_decode_string(response.UTF8String, response.length, &decodedResponse);
NSLog(@"Response: %@", @(decodedResponse));
if (decodedResponse)
{
free(decodedResponse);
}
return YES;
}
case 'T':
{
// Thread Information
NSLog(@"Thread stopped. Details:\n%s", response.UTF8String + 1);
// Parse thread state to determine if app is running.
NSArray<NSString *> *components = [response componentsSeparatedByString:@";"];
if (components.count <= 1)
{
if (error)
{
*error = [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:@{NSLocalizedFailureReasonErrorKey: response}];
}
return NO;
}
NSString *mainThread = components[1];
NSString *threadState = [[mainThread componentsSeparatedByString:@":"] lastObject];
NSScanner *scanner = [NSScanner scannerWithString:threadState];
unsigned long long mainThreadState = 0;
[scanner scanHexLongLong:&mainThreadState];
// If main thread state == 0, app is not running.
if (mainThreadState == 0)
{
if (error)
{
*error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorRequestedAppNotRunning userInfo:nil];
}
return NO;
}
return YES;
}
case 'E':
{
// Error
if (error)
{
NSInteger errorCode = [[[response componentsSeparatedByString:@";"] firstObject] integerValue];
switch (errorCode)
{
case 96:
*error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorRequestedAppNotRunning userInfo:nil];
break;
default:
*error = [NSError errorWithDomain:AltServerConnectionErrorDomain code:ALTServerConnectionErrorUnknown userInfo:@{NSLocalizedFailureReasonErrorKey: response}];
break;
}
}
return NO;
}
case 'W':
{
// Warning
NSLog(@"WARNING: %@", response);
return YES;
}
}
return YES;
}
@end

View File

@@ -1,24 +0,0 @@
//
// ALTNotificationConnection+Private.h
// AltServer
//
// Created by Riley Testut on 1/10/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
#import "ALTNotificationConnection.h"
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/notification_proxy.h>
NS_ASSUME_NONNULL_BEGIN
@interface ALTNotificationConnection ()
@property (nonatomic, readonly) np_client_t client;
- (instancetype)initWithDevice:(ALTDevice *)device client:(np_client_t)client;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,30 +0,0 @@
//
// ALTNotificationConnection.h
// AltServer
//
// Created by Riley Testut on 1/10/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
#import "AltSign.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(NotificationConnection)
@interface ALTNotificationConnection : NSObject
@property (nonatomic, copy, readonly) ALTDevice *device;
@property (nonatomic, copy, nullable) void (^receivedNotificationHandler)(CFNotificationName notification);
- (void)startListeningForNotifications:(NSArray<NSString *> *)notifications
completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)sendNotification:(CFNotificationName)notification
completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)disconnect;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,94 +0,0 @@
//
// ALTNotificationConnection.m
// AltServer
//
// Created by Riley Testut on 1/10/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
#import "ALTNotificationConnection+Private.h"
#import "NSError+ALTServerError.h"
void ALTDeviceReceivedNotification(const char *notification, void *user_data);
@implementation ALTNotificationConnection
- (instancetype)initWithDevice:(ALTDevice *)device client:(np_client_t)client
{
self = [super init];
if (self)
{
_device = [device copy];
_client = client;
}
return self;
}
- (void)dealloc
{
[self disconnect];
}
- (void)disconnect
{
np_client_free(self.client);
_client = nil;
}
- (void)startListeningForNotifications:(NSArray<NSString *> *)notifications completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
{
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
const char **notificationNames = (const char **)malloc((notifications.count + 1) * sizeof(char *));
for (int i = 0; i < notifications.count; i++)
{
NSString *name = notifications[i];
notificationNames[i] = name.UTF8String;
}
notificationNames[notifications.count] = NULL; // Must have terminating NULL entry.
np_error_t result = np_observe_notifications(self.client, notificationNames);
if (result != NP_E_SUCCESS)
{
return completionHandler(NO, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
}
result = np_set_notify_callback(self.client, ALTDeviceReceivedNotification, (__bridge void *)self);
if (result != NP_E_SUCCESS)
{
return completionHandler(NO, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
}
completionHandler(YES, nil);
free(notificationNames);
});
}
- (void)sendNotification:(CFNotificationName)notification completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
{
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
np_error_t result = np_post_notification(self.client, [(__bridge NSString *)notification UTF8String]);
if (result == NP_E_SUCCESS)
{
completionHandler(YES, nil);
}
else
{
completionHandler(NO, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
}
});
}
@end
void ALTDeviceReceivedNotification(const char *notification, void *user_data)
{
ALTNotificationConnection *connection = (__bridge ALTNotificationConnection *)user_data;
if (connection.receivedNotificationHandler)
{
connection.receivedNotificationHandler((__bridge CFNotificationName)@(notification));
}
}

View File

@@ -1,25 +0,0 @@
//
// ALTWiredConnection+Private.h
// AltServer
//
// Created by Riley Testut on 1/10/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
#import "ALTWiredConnection.h"
#include <libimobiledevice/libimobiledevice.h>
NS_ASSUME_NONNULL_BEGIN
@interface ALTWiredConnection ()
@property (nonatomic, readwrite, getter=isConnected) BOOL connected;
@property (nonatomic, readonly) idevice_connection_t connection;
- (instancetype)initWithDevice:(ALTDevice *)device connection:(idevice_connection_t)connection;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,29 +0,0 @@
//
// ALTWiredConnection.h
// AltServer
//
// Created by Riley Testut on 1/10/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
#import "AltSign.h"
#import "ALTConnection.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(WiredConnection)
@interface ALTWiredConnection : NSObject <ALTConnection>
@property (nonatomic, readonly, getter=isConnected) BOOL connected;
@property (nonatomic, copy, readonly) ALTDevice *device;
- (void)sendData:(NSData *)data completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler;
- (void)receiveDataWithExpectedSize:(NSInteger)expectedSize completionHandler:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler;
- (void)disconnect;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,119 +0,0 @@
//
// ALTWiredConnection.m
// AltServer
//
// Created by Riley Testut on 1/10/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
#import "ALTWiredConnection+Private.h"
#import "ALTConnection.h"
#import "NSError+ALTServerError.h"
@implementation ALTWiredConnection
- (instancetype)initWithDevice:(ALTDevice *)device connection:(idevice_connection_t)connection
{
self = [super init];
if (self)
{
_device = [device copy];
_connection = connection;
self.connected = YES;
}
return self;
}
- (void)dealloc
{
[self disconnect];
}
- (void)disconnect
{
if (![self isConnected])
{
return;
}
idevice_disconnect(self.connection);
_connection = nil;
self.connected = NO;
}
- (void)sendData:(NSData *)data completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
{
void (^finish)(NSError *error) = ^(NSError *error) {
if (error != nil)
{
NSLog(@"Send Error: %@", error);
completionHandler(NO, error);
}
else
{
completionHandler(YES, nil);
}
};
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
NSMutableData *mutableData = [data mutableCopy];
while (mutableData.length > 0)
{
uint32_t sentBytes = 0;
if (idevice_connection_send(self.connection, (const char *)mutableData.bytes, (int32_t)mutableData.length, &sentBytes) != IDEVICE_E_SUCCESS)
{
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
}
[mutableData replaceBytesInRange:NSMakeRange(0, sentBytes) withBytes:NULL length:0];
}
finish(nil);
});
}
- (void)receiveDataWithExpectedSize:(NSInteger)expectedSize completionHandler:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler
{
void (^finish)(NSData *data, NSError *error) = ^(NSData *data, NSError *error) {
if (error != nil)
{
NSLog(@"Receive Data Error: %@", error);
}
completionHandler(data, error);
};
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
char bytes[4096];
NSMutableData *receivedData = [NSMutableData dataWithCapacity:expectedSize];
while (receivedData.length < expectedSize)
{
uint32_t size = MIN(4096, (uint32_t)expectedSize - (uint32_t)receivedData.length);
uint32_t receivedBytes = 0;
if (idevice_connection_receive_timeout(self.connection, bytes, size, &receivedBytes, 10000) != IDEVICE_E_SUCCESS)
{
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
}
NSData *data = [NSData dataWithBytesNoCopy:bytes length:receivedBytes freeWhenDone:NO];
[receivedData appendData:data];
}
finish(receivedData, nil);
});
}
#pragma mark - NSObject -
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ (Wired)", self.device.name];
}
@end

View File

@@ -1,263 +0,0 @@
//
// RequestHandler.swift
// AltServer
//
// Created by Riley Testut on 5/23/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
typealias ServerConnectionManager = ConnectionManager<ServerRequestHandler>
private let connectionManager = ConnectionManager(requestHandler: ServerRequestHandler(),
connectionHandlers: [WirelessConnectionHandler(), WiredConnectionHandler()])
extension ServerConnectionManager
{
static var shared: ConnectionManager {
return connectionManager
}
}
struct ServerRequestHandler: RequestHandler
{
func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: Connection, completionHandler: @escaping (Result<AnisetteDataResponse, Error>) -> Void)
{
AnisetteDataManager.shared.requestAnisetteData { (result) in
switch result
{
case .failure(let error): completionHandler(.failure(error))
case .success(let anisetteData):
let response = AnisetteDataResponse(anisetteData: anisetteData)
completionHandler(.success(response))
}
}
}
func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: Connection, completionHandler: @escaping (Result<InstallationProgressResponse, Error>) -> Void)
{
var temporaryURL: URL?
func finish(_ result: Result<InstallationProgressResponse, Error>)
{
if let temporaryURL = temporaryURL
{
do { try FileManager.default.removeItem(at: temporaryURL) }
catch { print("Failed to remove .ipa.", error) }
}
completionHandler(result)
}
self.receiveApp(for: request, from: connection) { (result) in
print("Received app with result:", result)
switch result
{
case .failure(let error): finish(.failure(error))
case .success(let fileURL):
temporaryURL = fileURL
print("Awaiting begin installation request...")
connection.receiveRequest() { (result) in
print("Received begin installation request with result:", result)
switch result
{
case .failure(let error): finish(.failure(error))
case .success(.beginInstallation(let installRequest)):
print("Installing app to device \(request.udid)...")
self.installApp(at: fileURL, toDeviceWithUDID: request.udid, activeProvisioningProfiles: installRequest.activeProfiles, connection: connection) { (result) in
print("Installed app to device with result:", result)
switch result
{
case .failure(let error): finish(.failure(error))
case .success:
let response = InstallationProgressResponse(progress: 1.0)
finish(.success(response))
}
}
case .success: finish(.failure(ALTServerError(.unknownRequest)))
}
}
}
}
}
func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: Connection,
completionHandler: @escaping (Result<InstallProvisioningProfilesResponse, Error>) -> Void)
{
ALTDeviceManager.shared.installProvisioningProfiles(request.provisioningProfiles, toDeviceWithUDID: request.udid, activeProvisioningProfiles: request.activeProfiles) { (success, error) in
if let error = error, !success
{
print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", error)
completionHandler(.failure(ALTServerError(error)))
}
else
{
print("Installed profiles:", request.provisioningProfiles.map { $0.bundleIdentifier })
let response = InstallProvisioningProfilesResponse()
completionHandler(.success(response))
}
}
}
func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: Connection,
completionHandler: @escaping (Result<RemoveProvisioningProfilesResponse, Error>) -> Void)
{
ALTDeviceManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers, fromDeviceWithUDID: request.udid) { (success, error) in
if let error = error, !success
{
print("Failed to remove profiles \(request.bundleIdentifiers):", error)
completionHandler(.failure(ALTServerError(error)))
}
else
{
print("Removed profiles:", request.bundleIdentifiers)
let response = RemoveProvisioningProfilesResponse()
completionHandler(.success(response))
}
}
}
func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: Connection, completionHandler: @escaping (Result<RemoveAppResponse, Error>) -> Void)
{
ALTDeviceManager.shared.removeApp(forBundleIdentifier: request.bundleIdentifier, fromDeviceWithUDID: request.udid) { (success, error) in
if let error = error, !success
{
print("Failed to remove app \(request.bundleIdentifier):", error)
completionHandler(.failure(ALTServerError(error)))
}
else
{
print("Removed app:", request.bundleIdentifier)
let response = RemoveAppResponse()
completionHandler(.success(response))
}
}
}
func handleEnableUnsignedCodeExecutionRequest(_ request: EnableUnsignedCodeExecutionRequest, for connection: Connection, completionHandler: @escaping (Result<EnableUnsignedCodeExecutionResponse, Error>) -> Void)
{
guard let device = ALTDeviceManager.shared.availableDevices.first(where: { $0.identifier == request.udid }) else { return completionHandler(.failure(ALTServerError(.deviceNotFound))) }
ALTDeviceManager.shared.prepare(device) { result in
switch result
{
case .failure(let error): completionHandler(.failure(error))
case .success:
ALTDeviceManager.shared.startDebugConnection(to: device) { (connection, error) in
guard let connection = connection else { return completionHandler(.failure(error!)) }
func finish(success: Bool, error: Error?)
{
if let error = error, !success
{
print("Failed to enable unsigned code execution for process \(request.processID?.description ?? request.processName ?? "nil"):", error)
completionHandler(.failure(ALTServerError(error)))
}
else
{
print("Enabled unsigned code execution for process:", request.processID ?? request.processName ?? "nil")
let response = EnableUnsignedCodeExecutionResponse()
completionHandler(.success(response))
}
}
if let processID = request.processID
{
connection.enableUnsignedCodeExecutionForProcess(withID: processID, completionHandler: finish)
}
else if let processName = request.processName
{
connection.enableUnsignedCodeExecutionForProcess(withName: processName, completionHandler: finish)
}
else
{
finish(success: false, error: ALTServerError(.invalidRequest))
}
}
}
}
}
}
private extension RequestHandler
{
func receiveApp(for request: PrepareAppRequest, from connection: Connection, completionHandler: @escaping (Result<URL, ALTServerError>) -> Void)
{
connection.receiveData(expectedSize: request.contentSize) { (result) in
do
{
print("Received app data!")
let data = try result.get()
guard ALTDeviceManager.shared.availableDevices.contains(where: { $0.identifier == request.udid }) else { throw ALTServerError(.deviceNotFound) }
print("Writing app data...")
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".ipa")
try data.write(to: temporaryURL, options: .atomic)
print("Wrote app to URL:", temporaryURL)
completionHandler(.success(temporaryURL))
}
catch
{
print("Error processing app data:", error)
completionHandler(.failure(ALTServerError(error)))
}
}
}
func installApp(at fileURL: URL, toDeviceWithUDID udid: String, activeProvisioningProfiles: Set<String>?, connection: Connection, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
{
let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default)
var isSending = false
var observation: NSKeyValueObservation?
let progress = ALTDeviceManager.shared.installApp(at: fileURL, toDeviceWithUDID: udid, activeProvisioningProfiles: activeProvisioningProfiles) { (success, error) in
print("Installed app with result:", error == nil ? "Success" : error!.localizedDescription)
if let error = error.map({ ALTServerError($0) })
{
completionHandler(.failure(error))
}
else
{
completionHandler(.success(()))
}
observation?.invalidate()
observation = nil
}
observation = progress.observe(\.fractionCompleted, changeHandler: { (progress, change) in
serialQueue.async {
guard !isSending else { return }
isSending = true
print("Progress:", progress.fractionCompleted)
let response = InstallationProgressResponse(progress: progress.fractionCompleted)
connection.send(response) { (result) in
serialQueue.async {
isSending = false
}
}
}
})
}
}

View File

@@ -1,122 +0,0 @@
//
// WiredConnectionHandler.swift
// AltServer
//
// Created by Riley Testut on 6/1/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
class WiredConnectionHandler: ConnectionHandler
{
var connectionHandler: ((Connection) -> Void)?
var disconnectionHandler: ((Connection) -> Void)?
private var notificationConnections = [ALTDevice: NotificationConnection]()
private let queue = DispatchQueue(label: "WiredConnectionHandler", autoreleaseFrequency: .workItem, target: .global(qos: .utility))
func startListening()
{
NotificationCenter.default.addObserver(self, selector: #selector(WiredConnectionHandler.deviceDidConnect(_:)), name: .deviceManagerDeviceDidConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(WiredConnectionHandler.deviceDidDisconnect(_:)), name: .deviceManagerDeviceDidDisconnect, object: nil)
}
func stopListening()
{
NotificationCenter.default.removeObserver(self, name: .deviceManagerDeviceDidConnect, object: nil)
NotificationCenter.default.removeObserver(self, name: .deviceManagerDeviceDidDisconnect, object: nil)
}
}
private extension WiredConnectionHandler
{
func startNotificationConnection(to device: ALTDevice)
{
self.queue.async {
ALTDeviceManager.shared.startNotificationConnection(to: device) { (connection, error) in
guard let connection = connection else { return }
let notifications: [CFNotificationName] = [.wiredServerConnectionAvailableRequest, .wiredServerConnectionStartRequest]
connection.startListening(forNotifications: notifications.map { String($0.rawValue) }) { (success, error) in
guard success else { return }
self.queue.async {
connection.receivedNotificationHandler = { [weak self, weak connection] (notification) in
guard let self = self, let connection = connection else { return }
self.handle(notification, for: connection)
}
self.notificationConnections[device] = connection
}
}
}
}
}
func stopNotificationConnection(to device: ALTDevice)
{
self.queue.async {
guard let connection = self.notificationConnections[device] else { return }
connection.disconnect()
self.notificationConnections[device] = nil
}
}
func handle(_ notification: CFNotificationName, for connection: NotificationConnection)
{
switch notification
{
case .wiredServerConnectionAvailableRequest:
connection.sendNotification(.wiredServerConnectionAvailableResponse) { (success, error) in
if let error = error, !success
{
print("Error sending wired server connection response.", error)
}
else
{
print("Sent wired server connection available response!")
}
}
case .wiredServerConnectionStartRequest:
ALTDeviceManager.shared.startWiredConnection(to: connection.device) { (wiredConnection, error) in
if let wiredConnection = wiredConnection
{
print("Started wired server connection!")
self.connectionHandler?(wiredConnection)
var observation: NSKeyValueObservation?
observation = wiredConnection.observe(\.isConnected) { [weak self] (connection, change) in
guard !connection.isConnected else { return }
self?.disconnectionHandler?(connection)
observation?.invalidate()
}
}
else if let error = error
{
print("Error starting wired server connection.", error)
}
}
default: break
}
}
}
private extension WiredConnectionHandler
{
@objc func deviceDidConnect(_ notification: Notification)
{
guard let device = notification.object as? ALTDevice else { return }
self.startNotificationConnection(to: device)
}
@objc func deviceDidDisconnect(_ notification: Notification)
{
guard let device = notification.object as? ALTDevice else { return }
self.stopNotificationConnection(to: device)
}
}

View File

@@ -1,153 +0,0 @@
//
// WirelessConnectionHandler.swift
// AltKit
//
// Created by Riley Testut on 6/1/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
import Network
extension WirelessConnectionHandler
{
public enum State
{
case notRunning
case connecting
case running(NWListener.Service)
case failed(Swift.Error)
}
}
public class WirelessConnectionHandler: ConnectionHandler
{
public var connectionHandler: ((Connection) -> Void)?
public var disconnectionHandler: ((Connection) -> Void)?
public var stateUpdateHandler: ((State) -> Void)?
public private(set) var state: State = .notRunning {
didSet {
self.stateUpdateHandler?(self.state)
}
}
private lazy var listener = self.makeListener()
private let dispatchQueue = DispatchQueue(label: "io.altstore.WirelessConnectionListener", qos: .utility)
public func startListening()
{
switch self.state
{
case .notRunning, .failed: self.listener.start(queue: self.dispatchQueue)
default: break
}
}
public func stopListening()
{
switch self.state
{
case .running: self.listener.cancel()
default: break
}
}
}
private extension WirelessConnectionHandler
{
func makeListener() -> NWListener
{
let listener = try! NWListener(using: .tcp)
let service: NWListener.Service
if let serverID = UserDefaults.standard.serverID?.data(using: .utf8)
{
let txtDictionary = ["serverID": serverID]
let txtData = NetService.data(fromTXTRecord: txtDictionary)
service = NWListener.Service(name: nil, type: ALTServerServiceType, domain: nil, txtRecord: txtData)
}
else
{
service = NWListener.Service(type: ALTServerServiceType)
}
listener.service = service
listener.serviceRegistrationUpdateHandler = { (serviceChange) in
switch serviceChange
{
case .add(.service(let name, let type, let domain, _)):
let service = NWListener.Service(name: name, type: type, domain: domain, txtRecord: nil)
self.state = .running(service)
default: break
}
}
listener.stateUpdateHandler = { (state) in
switch state
{
case .ready: break
case .waiting, .setup: self.state = .connecting
case .cancelled: self.state = .notRunning
case .failed(let error): self.state = .failed(error)
@unknown default: break
}
}
listener.newConnectionHandler = { [weak self] (connection) in
self?.prepare(connection)
}
return listener
}
func prepare(_ nwConnection: NWConnection)
{
print("Preparing:", nwConnection)
// Use same instance for all callbacks.
let connection = NetworkConnection(nwConnection)
nwConnection.stateUpdateHandler = { [weak self] (state) in
switch state
{
case .setup, .preparing: break
case .ready:
print("Connected to client:", connection)
self?.connectionHandler?(connection)
case .waiting:
print("Waiting for connection...")
case .failed(let error):
print("Failed to connect to service \(nwConnection.endpoint).", error)
self?.disconnect(connection)
case .cancelled:
self?.disconnect(connection)
@unknown default: break
}
}
nwConnection.start(queue: self.dispatchQueue)
}
func disconnect(_ connection: Connection)
{
connection.disconnect()
self.disconnectionHandler?(connection)
if let networkConnection = connection as? NetworkConnection
{
networkConnection.nwConnection.stateUpdateHandler = nil
}
}
}

View File

@@ -1,327 +0,0 @@
//
// DeveloperDiskManager.swift
// AltServer
//
// Created by Riley Testut on 2/19/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
import Foundation
import AltSign
enum DeveloperDiskError: LocalizedError
{
case unknownDownloadURL
case unsupportedOperatingSystem
case downloadedDiskNotFound
var errorDescription: String? {
switch self
{
case .unknownDownloadURL: return NSLocalizedString("The URL to download the Developer disk image could not be determined.", comment: "")
case .unsupportedOperatingSystem: return NSLocalizedString("The device's operating system does not support installing Developer disk images.", comment: "")
case .downloadedDiskNotFound: return NSLocalizedString("DeveloperDiskImage.dmg and its signature could not be found in the downloaded archive.", comment: "")
}
}
}
private extension URL
{
#if STAGING
static let developerDiskDownloadURLs = URL(string: "https://f000.backblazeb2.com/file/altstore-staging/altserver/developerdisks.json")!
#else
static let developerDiskDownloadURLs = URL(string: "https://cdn.altstore.io/file/altstore/altserver/developerdisks.json")!
#endif
}
private extension DeveloperDiskManager
{
struct FetchURLsResponse: Decodable
{
struct Disks: Decodable
{
var iOS: [String: DeveloperDiskURL]?
var tvOS: [String: DeveloperDiskURL]?
}
var version: Int
var disks: Disks
}
enum DeveloperDiskURL: Decodable
{
case archive(URL)
case separate(diskURL: URL, signatureURL: URL)
private enum CodingKeys: CodingKey
{
case archive
case disk
case signature
}
init(from decoder: Decoder) throws
{
let container = try decoder.container(keyedBy: CodingKeys.self)
if container.contains(.archive)
{
let archiveURL = try container.decode(URL.self, forKey: .archive)
self = .archive(archiveURL)
}
else
{
let diskURL = try container.decode(URL.self, forKey: .disk)
let signatureURL = try container.decode(URL.self, forKey: .signature)
self = .separate(diskURL: diskURL, signatureURL: signatureURL)
}
}
}
}
class DeveloperDiskManager
{
private let session = URLSession(configuration: .ephemeral)
func downloadDeveloperDisk(for device: ALTDevice, completionHandler: @escaping (Result<(URL, URL), Error>) -> Void)
{
do
{
guard let osName = device.type.osName else { throw DeveloperDiskError.unsupportedOperatingSystem }
let osKeyPath: KeyPath<FetchURLsResponse.Disks, [String: DeveloperDiskURL]?>
switch device.type
{
case .iphone, .ipad: osKeyPath = \FetchURLsResponse.Disks.iOS
case .appletv: osKeyPath = \FetchURLsResponse.Disks.tvOS
default: throw DeveloperDiskError.unsupportedOperatingSystem
}
var osVersion = device.osVersion
osVersion.patchVersion = 0 // Patch is irrelevant for developer disks
let osDirectoryURL = FileManager.default.developerDisksDirectory.appendingPathComponent(osName)
let developerDiskDirectoryURL = osDirectoryURL.appendingPathComponent(osVersion.stringValue, isDirectory: true)
try FileManager.default.createDirectory(at: developerDiskDirectoryURL, withIntermediateDirectories: true, attributes: nil)
let developerDiskURL = developerDiskDirectoryURL.appendingPathComponent("DeveloperDiskImage.dmg")
let developerDiskSignatureURL = developerDiskDirectoryURL.appendingPathComponent("DeveloperDiskImage.dmg.signature")
let isCachedDiskCompatible = self.isDeveloperDiskCompatible(with: device)
if isCachedDiskCompatible && FileManager.default.fileExists(atPath: developerDiskURL.path) && FileManager.default.fileExists(atPath: developerDiskSignatureURL.path)
{
// The developer disk is cached and we've confirmed it works, so re-use it.
return completionHandler(.success((developerDiskURL, developerDiskSignatureURL)))
}
func finish(_ result: Result<(URL, URL), Error>)
{
do
{
let (diskFileURL, signatureFileURL) = try result.get()
if FileManager.default.fileExists(atPath: developerDiskURL.path)
{
try FileManager.default.removeItem(at: developerDiskURL)
}
if FileManager.default.fileExists(atPath: developerDiskSignatureURL.path)
{
try FileManager.default.removeItem(at: developerDiskSignatureURL)
}
try FileManager.default.copyItem(at: diskFileURL, to: developerDiskURL)
try FileManager.default.copyItem(at: signatureFileURL, to: developerDiskSignatureURL)
completionHandler(.success((developerDiskURL, developerDiskSignatureURL)))
}
catch
{
completionHandler(.failure(error))
}
}
self.fetchDeveloperDiskURLs { (result) in
do
{
let developerDiskURLs = try result.get()
guard let diskURL = developerDiskURLs[keyPath: osKeyPath]?[osVersion.stringValue] else { throw DeveloperDiskError.unknownDownloadURL }
switch diskURL
{
case .archive(let archiveURL): self.downloadDiskArchive(from: archiveURL, completionHandler: finish(_:))
case .separate(let diskURL, let signatureURL): self.downloadDisk(from: diskURL, signatureURL: signatureURL, completionHandler: finish(_:))
}
}
catch
{
finish(.failure(error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
func setDeveloperDiskCompatible(_ isCompatible: Bool, with device: ALTDevice)
{
guard let id = self.developerDiskCompatibilityID(for: device) else { return }
UserDefaults.standard.set(isCompatible, forKey: id)
}
func isDeveloperDiskCompatible(with device: ALTDevice) -> Bool
{
guard let id = self.developerDiskCompatibilityID(for: device) else { return false }
let isCompatible = UserDefaults.standard.bool(forKey: id)
return isCompatible
}
}
private extension DeveloperDiskManager
{
func developerDiskCompatibilityID(for device: ALTDevice) -> String?
{
guard let osName = device.type.osName else { return nil }
var osVersion = device.osVersion
osVersion.patchVersion = 0 // Patch is irrelevant for developer disks
let id = ["ALTDeveloperDiskCompatible", osName, device.osVersion.stringValue].joined(separator: "_")
return id
}
func fetchDeveloperDiskURLs(completionHandler: @escaping (Result<FetchURLsResponse.Disks, Error>) -> Void)
{
let dataTask = self.session.dataTask(with: .developerDiskDownloadURLs) { (data, response, error) in
do
{
guard let data = data else { throw error! }
let response = try JSONDecoder().decode(FetchURLsResponse.self, from: data)
completionHandler(.success(response.disks))
}
catch
{
completionHandler(.failure(error))
}
}
dataTask.resume()
}
func downloadDiskArchive(from url: URL, completionHandler: @escaping (Result<(URL, URL), Error>) -> Void)
{
let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in
do
{
guard let fileURL = fileURL else { throw error! }
defer { try? FileManager.default.removeItem(at: fileURL) }
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
defer { try? FileManager.default.removeItem(at: temporaryDirectory) }
try FileManager.default.unzipArchive(at: fileURL, toDirectory: temporaryDirectory)
guard let enumerator = FileManager.default.enumerator(at: temporaryDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
throw CocoaError(.fileNoSuchFile, userInfo: [NSURLErrorKey: temporaryDirectory])
}
var tempDiskFileURL: URL?
var tempSignatureFileURL: URL?
for case let fileURL as URL in enumerator
{
switch fileURL.pathExtension.lowercased()
{
case "dmg": tempDiskFileURL = fileURL
case "signature": tempSignatureFileURL = fileURL
default: break
}
}
guard let diskFileURL = tempDiskFileURL, let signatureFileURL = tempSignatureFileURL else { throw DeveloperDiskError.downloadedDiskNotFound }
completionHandler(.success((diskFileURL, signatureFileURL)))
}
catch
{
completionHandler(.failure(error))
}
}
downloadTask.resume()
}
func downloadDisk(from diskURL: URL, signatureURL: URL, completionHandler: @escaping (Result<(URL, URL), Error>) -> Void)
{
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
do { try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil) }
catch { return completionHandler(.failure(error)) }
var diskFileURL: URL?
var signatureFileURL: URL?
var downloadError: Error?
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
dispatchGroup.enter()
let diskDownloadTask = URLSession.shared.downloadTask(with: diskURL) { (fileURL, response, error) in
do
{
guard let fileURL = fileURL else { throw error! }
let destinationURL = temporaryDirectory.appendingPathComponent("DeveloperDiskImage.dmg")
try FileManager.default.copyItem(at: fileURL, to: destinationURL)
diskFileURL = destinationURL
}
catch
{
downloadError = error
}
dispatchGroup.leave()
}
let signatureDownloadTask = URLSession.shared.downloadTask(with: signatureURL) { (fileURL, response, error) in
do
{
guard let fileURL = fileURL else { throw error! }
let destinationURL = temporaryDirectory.appendingPathComponent("DeveloperDiskImage.dmg.signature")
try FileManager.default.copyItem(at: fileURL, to: destinationURL)
signatureFileURL = destinationURL
}
catch
{
downloadError = error
}
dispatchGroup.leave()
}
diskDownloadTask.resume()
signatureDownloadTask.resume()
dispatchGroup.notify(queue: .global(qos: .userInitiated)) {
defer {
try? FileManager.default.removeItem(at: temporaryDirectory)
}
guard let diskFileURL = diskFileURL, let signatureFileURL = signatureFileURL else {
return completionHandler(.failure(downloadError ?? DeveloperDiskError.downloadedDiskNotFound))
}
completionHandler(.success((diskFileURL, signatureFileURL)))
}
}
}

View File

@@ -1,941 +0,0 @@
//
// ALTDeviceManager+Installation.swift
// AltServer
//
// Created by Riley Testut on 7/1/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Cocoa
import UserNotifications
import ObjectiveC
private let appGroupsSemaphore = DispatchSemaphore(value: 1)
private let developerDiskManager = DeveloperDiskManager()
enum InstallError: Int, LocalizedError, _ObjectiveCBridgeableError
{
case cancelled
case noTeam
case missingPrivateKey
case missingCertificate
var errorDescription: String? {
switch self
{
case .cancelled: return NSLocalizedString("The operation was cancelled.", comment: "")
case .noTeam: return "You are not a member of any developer teams."
case .missingPrivateKey: return "The developer certificate's private key could not be found."
case .missingCertificate: return "The developer certificate could not be found."
}
}
init?(_bridgedNSError error: NSError)
{
guard error.domain == InstallError.cancelled._domain else { return nil }
if let installError = InstallError(rawValue: error.code)
{
self = installError
}
else
{
return nil
}
}
}
extension ALTDeviceManager
{
func installApplication(at url: URL, to altDevice: ALTDevice, appleID: String, password: String, completion: @escaping (Result<ALTApplication, Error>) -> Void)
{
let destinationDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
var appName = (url.isFileURL) ? url.deletingPathExtension().lastPathComponent : NSLocalizedString("AltStore", comment: "")
func finish(_ result: Result<ALTApplication, Error>, title: String = "")
{
DispatchQueue.main.async {
switch result
{
case .success(let app): completion(.success(app))
case .failure(var error as NSError):
if error.localizedFailure == nil
{
error = error.withLocalizedFailure(String(format: NSLocalizedString("Could not install %@ to %@.", comment: ""), appName, altDevice.name))
}
completion(.failure(error))
}
}
try? FileManager.default.removeItem(at: destinationDirectoryURL)
}
AnisetteDataManager.shared.requestAnisetteData { (result) in
do
{
let anisetteData = try result.get()
self.authenticate(appleID: appleID, password: password, anisetteData: anisetteData) { (result) in
do
{
let (account, session) = try result.get()
self.fetchTeam(for: account, session: session) { (result) in
do
{
let team = try result.get()
self.register(altDevice, team: team, session: session) { (result) in
do
{
let device = try result.get()
device.osVersion = altDevice.osVersion
self.fetchCertificate(for: team, session: session) { (result) in
do
{
let certificate = try result.get()
if !url.isFileURL
{
// Show alert before downloading remote .ipa.
self.showInstallationAlert(appName: NSLocalizedString("AltStore", comment: ""), deviceName: device.name)
}
self.prepare(device) { (result) in
switch result
{
case .failure(let error):
print("Failed to install DeveloperDiskImage.dmg to \(device).", error)
fallthrough // Continue installing app even if we couldn't install Developer disk image.
case .success:
self.downloadApp(from: url) { (result) in
do
{
let fileURL = try result.get()
try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil)
let appBundleURL = try FileManager.default.unzipAppBundle(at: fileURL, toDirectory: destinationDirectoryURL)
guard let application = ALTApplication(fileURL: appBundleURL) else { throw ALTError(.invalidApp) }
if url.isFileURL
{
// Show alert after "downloading" local .ipa.
self.showInstallationAlert(appName: application.name, deviceName: device.name)
}
appName = application.name
// Refresh anisette data to prevent session timeouts.
AnisetteDataManager.shared.requestAnisetteData { (result) in
do
{
let anisetteData = try result.get()
session.anisetteData = anisetteData
self.prepareAllProvisioningProfiles(for: application, device: device, team: team, session: session) { (result) in
do
{
let profiles = try result.get()
self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in
finish(result.map { application }, title: "Failed to Install AltStore")
}
}
catch
{
finish(.failure(error), title: "Failed to Fetch Provisioning Profiles")
}
}
}
catch
{
finish(.failure(error), title: "Failed to Refresh Anisette Data")
}
}
}
catch
{
finish(.failure(error), title: "Failed to Download AltStore")
}
}
}
}
}
catch
{
finish(.failure(error), title: "Failed to Fetch Certificate")
}
}
}
catch
{
finish(.failure(error), title: "Failed to Register Device")
}
}
}
catch
{
finish(.failure(error), title: "Failed to Fetch Team")
}
}
}
catch
{
finish(.failure(error), title: "Failed to Authenticate")
}
}
}
catch
{
finish(.failure(error), title: "Failed to Fetch Anisette Data")
}
}
}
}
extension ALTDeviceManager
{
func prepare(_ device: ALTDevice, completionHandler: @escaping (Result<Void, Error>) -> Void)
{
ALTDeviceManager.shared.isDeveloperDiskImageMounted(for: device) { (isMounted, error) in
switch (isMounted, error)
{
case (_, let error?): return completionHandler(.failure(error))
case (true, _): return completionHandler(.success(()))
case (false, _):
developerDiskManager.downloadDeveloperDisk(for: device) { (result) in
switch result
{
case .failure(let error): completionHandler(.failure(error))
case .success((let diskFileURL, let signatureFileURL)):
ALTDeviceManager.shared.installDeveloperDiskImage(at: diskFileURL, signatureURL: signatureFileURL, to: device) { (success, error) in
switch Result(success, error)
{
case .failure(let error as ALTServerError) where error.code == .incompatibleDeveloperDisk:
developerDiskManager.setDeveloperDiskCompatible(false, with: device)
completionHandler(.failure(error))
case .failure(let error):
// Don't mark developer disk as incompatible because it probably failed for a different reason.
completionHandler(.failure(error))
case .success:
developerDiskManager.setDeveloperDiskCompatible(true, with: device)
completionHandler(.success(()))
}
}
}
}
}
}
}
}
private extension ALTDeviceManager
{
func downloadApp(from url: URL, completionHandler: @escaping (Result<URL, Error>) -> Void)
{
guard !url.isFileURL else { return completionHandler(.success(url)) }
let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in
do
{
let (fileURL, _) = try Result((fileURL, response), error).get()
completionHandler(.success(fileURL))
do { try FileManager.default.removeItem(at: fileURL) }
catch { print("Failed to remove downloaded .ipa.", error) }
}
catch
{
completionHandler(.failure(error))
}
}
downloadTask.resume()
}
func authenticate(appleID: String, password: String, anisetteData: ALTAnisetteData, completionHandler: @escaping (Result<(ALTAccount, ALTAppleAPISession), Error>) -> Void)
{
func handleVerificationCode(_ completionHandler: @escaping (String?) -> Void)
{
DispatchQueue.main.async {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Two-Factor Authentication Enabled", comment: "")
alert.informativeText = NSLocalizedString("Please enter the 6-digit verification code that was sent to your Apple devices.", comment: "")
let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 22))
textField.delegate = self
textField.translatesAutoresizingMaskIntoConstraints = false
textField.placeholderString = NSLocalizedString("123456", comment: "")
alert.accessoryView = textField
alert.window.initialFirstResponder = textField
alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
self.securityCodeAlert = alert
self.securityCodeTextField = textField
self.validate()
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
let response = alert.runModal()
if response == .alertFirstButtonReturn
{
let code = textField.stringValue
completionHandler(code)
}
else
{
completionHandler(nil)
}
}
}
ALTAppleAPI.shared.authenticate(appleID: appleID, password: password, anisetteData: anisetteData, verificationHandler: handleVerificationCode) { (account, session, error) in
if let account = account, let session = session
{
completionHandler(.success((account, session)))
}
else
{
completionHandler(.failure(error ?? ALTAppleAPIError(.unknown)))
}
}
}
func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Error>) -> Void)
{
ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in
do
{
let teams = try Result(teams, error).get()
if let team = teams.first(where: { $0.type == .individual })
{
return completionHandler(.success(team))
}
else if let team = teams.first(where: { $0.type == .free })
{
return completionHandler(.success(team))
}
else if let team = teams.first
{
return completionHandler(.success(team))
}
else
{
throw InstallError.noTeam
}
}
catch
{
completionHandler(.failure(error))
}
}
}
func fetchCertificate(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTCertificate, Error>) -> Void)
{
ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in
do
{
let certificates = try Result(certificates, error).get()
let certificateFileURL = FileManager.default.certificatesDirectory.appendingPathComponent(team.identifier + ".p12")
try FileManager.default.createDirectory(at: FileManager.default.certificatesDirectory, withIntermediateDirectories: true, attributes: nil)
var isCancelled = false
// Check if there is another AltStore certificate, which means AltStore has been installed with this Apple ID before.
let altstoreCertificate = certificates.first { $0.machineName?.starts(with: "AltStore") == true }
if let previousCertificate = altstoreCertificate
{
if FileManager.default.fileExists(atPath: certificateFileURL.path),
let data = try? Data(contentsOf: certificateFileURL),
let certificate = ALTCertificate(p12Data: data, password: previousCertificate.machineIdentifier)
{
// Manually set machineIdentifier so we can encrypt + embed certificate if needed.
certificate.machineIdentifier = previousCertificate.machineIdentifier
return completionHandler(.success(certificate))
}
DispatchQueue.main.sync {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Multiple AltServers Not Supported", comment: "")
alert.informativeText = NSLocalizedString("Please use the same AltServer you previously used with this Apple ID, or else apps installed with other AltServers will stop working.\n\nAre you sure you want to continue?", comment: "")
alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
let buttonIndex = alert.runModal()
if buttonIndex == NSApplication.ModalResponse.alertSecondButtonReturn
{
isCancelled = true
}
}
guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) }
}
func addCertificate()
{
ALTAppleAPI.shared.addCertificate(machineName: "AltStore", to: team, session: session) { (certificate, error) in
do
{
let certificate = try Result(certificate, error).get()
guard let privateKey = certificate.privateKey else { throw InstallError.missingPrivateKey }
ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in
do
{
let certificates = try Result(certificates, error).get()
guard let certificate = certificates.first(where: { $0.serialNumber == certificate.serialNumber }) else {
throw InstallError.missingCertificate
}
certificate.privateKey = privateKey
completionHandler(.success(certificate))
if let machineIdentifier = certificate.machineIdentifier,
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
{
// Cache certificate.
do { try encryptedData.write(to: certificateFileURL, options: .atomic) }
catch { print("Failed to cache certificate:", error) }
}
}
catch
{
completionHandler(.failure(error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
if let certificate = altstoreCertificate ?? certificates.first
{
if team.type != .free
{
DispatchQueue.main.sync {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Installing this app will revoke your iOS development certificate.", comment: "")
alert.informativeText = NSLocalizedString("""
This will not affect apps you've submitted to the App Store, but may cause apps you've installed to your devices with Xcode to stop working until you reinstall them.
To prevent this from happening, feel free to try again with another Apple ID.
""", comment: "")
alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
let buttonIndex = alert.runModal()
if buttonIndex == NSApplication.ModalResponse.alertSecondButtonReturn
{
isCancelled = true
}
}
guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) }
}
ALTAppleAPI.shared.revoke(certificate, for: team, session: session) { (success, error) in
do
{
try Result(success, error).get()
addCertificate()
}
catch
{
completionHandler(.failure(error))
}
}
}
else
{
addCertificate()
}
}
catch
{
completionHandler(.failure(error))
}
}
}
func prepareAllProvisioningProfiles(for application: ALTApplication, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession,
completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void)
{
self.prepareProvisioningProfile(for: application, parentApp: nil, device: device, team: team, session: session) { (result) in
do
{
let profile = try result.get()
var profiles = [application.bundleIdentifier: profile]
var error: Error?
let dispatchGroup = DispatchGroup()
for appExtension in application.appExtensions
{
dispatchGroup.enter()
self.prepareProvisioningProfile(for: appExtension, parentApp: application, device: device, team: team, session: session) { (result) in
switch result
{
case .failure(let e): error = e
case .success(let profile): profiles[appExtension.bundleIdentifier] = profile
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .global()) {
if let error = error
{
completion(.failure(error))
}
else
{
completion(.success(profiles))
}
}
}
catch
{
completion(.failure(error))
}
}
}
func prepareProvisioningProfile(for application: ALTApplication, parentApp: ALTApplication?, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
{
let parentBundleID = parentApp?.bundleIdentifier ?? application.bundleIdentifier
let updatedParentBundleID: String
if application.isAltStoreApp
{
// Use legacy bundle ID format for AltStore (and its extensions).
updatedParentBundleID = "com.\(team.identifier).\(parentBundleID)"
}
else
{
updatedParentBundleID = parentBundleID + "." + team.identifier // Append just team identifier to make it harder to track.
}
let bundleID = application.bundleIdentifier.replacingOccurrences(of: parentBundleID, with: updatedParentBundleID)
let preferredName: String
if let parentApp = parentApp
{
preferredName = parentApp.name + " " + application.name
}
else
{
preferredName = application.name
}
self.registerAppID(name: preferredName, bundleID: bundleID, team: team, session: session) { (result) in
do
{
let appID = try result.get()
self.updateFeatures(for: appID, app: application, team: team, session: session) { (result) in
do
{
let appID = try result.get()
self.updateAppGroups(for: appID, app: application, team: team, session: session) { (result) in
do
{
let appID = try result.get()
self.fetchProvisioningProfile(for: appID, device: device, team: team, session: session) { (result) in
completionHandler(result)
}
}
catch
{
completionHandler(.failure(error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
func registerAppID(name appName: String, bundleID: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
{
ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in
do
{
let appIDs = try Result(appIDs, error).get()
if let appID = appIDs.first(where: { $0.bundleIdentifier == bundleID })
{
completionHandler(.success(appID))
}
else
{
ALTAppleAPI.shared.addAppID(withName: appName, bundleIdentifier: bundleID, team: team, session: session) { (appID, error) in
completionHandler(Result(appID, error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
func updateFeatures(for appID: ALTAppID, app: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
{
let requiredFeatures = app.entitlements.compactMap { (entitlement, value) -> (ALTFeature, Any)? in
guard let feature = ALTFeature(entitlement: entitlement) else { return nil }
return (feature, value)
}
var features = requiredFeatures.reduce(into: [ALTFeature: Any]()) { $0[$1.0] = $1.1 }
if let applicationGroups = app.entitlements[.appGroups] as? [String], !applicationGroups.isEmpty
{
// App uses app groups, so assign `true` to enable the feature.
features[.appGroups] = true
}
else
{
// App has no app groups, so assign `false` to disable the feature.
features[.appGroups] = false
}
var updateFeatures = false
// Determine whether the required features are already enabled for the AppID.
for (feature, value) in features
{
if let appIDValue = appID.features[feature] as AnyObject?, (value as AnyObject).isEqual(appIDValue)
{
// AppID already has this feature enabled and the values are the same.
continue
}
else if appID.features[feature] == nil, let shouldEnableFeature = value as? Bool, !shouldEnableFeature
{
// AppID doesn't already have this feature enabled, but we want it disabled anyway.
continue
}
else
{
// AppID either doesn't have this feature enabled or the value has changed,
// so we need to update it to reflect new values.
updateFeatures = true
break
}
}
if updateFeatures
{
let appID = appID.copy() as! ALTAppID
appID.features = features
ALTAppleAPI.shared.update(appID, team: team, session: session) { (appID, error) in
completionHandler(Result(appID, error))
}
}
else
{
completionHandler(.success(appID))
}
}
func updateAppGroups(for appID: ALTAppID, app: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
{
guard let applicationGroups = app.entitlements[.appGroups] as? [String], !applicationGroups.isEmpty else {
// Assigning an App ID to an empty app group array fails,
// so just do nothing if there are no app groups.
return completionHandler(.success(appID))
}
// Dispatch onto global queue to prevent appGroupsSemaphore deadlock.
DispatchQueue.global().async {
// Ensure we're not concurrently fetching and updating app groups,
// which can lead to race conditions such as adding an app group twice.
appGroupsSemaphore.wait()
func finish(_ result: Result<ALTAppID, Error>)
{
appGroupsSemaphore.signal()
completionHandler(result)
}
ALTAppleAPI.shared.fetchAppGroups(for: team, session: session) { (groups, error) in
switch Result(groups, error)
{
case .failure(let error): finish(.failure(error))
case .success(let fetchedGroups):
let dispatchGroup = DispatchGroup()
var groups = [ALTAppGroup]()
var errors = [Error]()
for groupIdentifier in applicationGroups
{
let adjustedGroupIdentifier = groupIdentifier + "." + team.identifier
if let group = fetchedGroups.first(where: { $0.groupIdentifier == adjustedGroupIdentifier })
{
groups.append(group)
}
else
{
dispatchGroup.enter()
// Not all characters are allowed in group names, so we replace periods with spaces (like Apple does).
let name = "AltStore " + groupIdentifier.replacingOccurrences(of: ".", with: " ")
ALTAppleAPI.shared.addAppGroup(withName: name, groupIdentifier: adjustedGroupIdentifier, team: team, session: session) { (group, error) in
switch Result(group, error)
{
case .success(let group): groups.append(group)
case .failure(let error): errors.append(error)
}
dispatchGroup.leave()
}
}
}
dispatchGroup.notify(queue: .global()) {
if let error = errors.first
{
finish(.failure(error))
}
else
{
ALTAppleAPI.shared.assign(appID, to: Array(groups), team: team, session: session) { (success, error) in
let result = Result(success, error)
finish(result.map { _ in appID })
}
}
}
}
}
}
}
func register(_ device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> Void)
{
ALTAppleAPI.shared.fetchDevices(for: team, types: device.type, session: session) { (devices, error) in
do
{
let devices = try Result(devices, error).get()
if let device = devices.first(where: { $0.identifier == device.identifier })
{
completionHandler(.success(device))
}
else
{
ALTAppleAPI.shared.registerDevice(name: device.name, identifier: device.identifier, type: device.type, team: team, session: session) { (device, error) in
completionHandler(Result(device, error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
func fetchProvisioningProfile(for appID: ALTAppID, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
{
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: device.type, team: team, session: session) { (profile, error) in
completionHandler(Result(profile, error))
}
}
func install(_ application: ALTApplication, to device: ALTDevice, team: ALTTeam, certificate: ALTCertificate, profiles: [String: ALTProvisioningProfile], completionHandler: @escaping (Result<Void, Error>) -> Void)
{
func prepare(_ bundle: Bundle, additionalInfoDictionaryValues: [String: Any] = [:]) throws
{
guard let identifier = bundle.bundleIdentifier else { throw ALTError(.missingAppBundle) }
guard let profile = profiles[identifier] else { throw ALTError(.missingProvisioningProfile) }
guard var infoDictionary = bundle.completeInfoDictionary else { throw ALTError(.missingInfoPlist) }
infoDictionary[kCFBundleIdentifierKey as String] = profile.bundleIdentifier
infoDictionary[Bundle.Info.altBundleID] = identifier
if (infoDictionary.keys.contains(Bundle.Info.deviceID)) {
infoDictionary[Bundle.Info.deviceID] = device.identifier
}
for (key, value) in additionalInfoDictionaryValues
{
infoDictionary[key] = value
}
if let appGroups = profile.entitlements[.appGroups] as? [String]
{
infoDictionary[Bundle.Info.appGroups] = appGroups
}
try (infoDictionary as NSDictionary).write(to: bundle.infoPlistURL)
}
DispatchQueue.global().async {
do
{
guard let appBundle = Bundle(url: application.fileURL) else { throw ALTError(.missingAppBundle) }
guard let infoDictionary = appBundle.completeInfoDictionary else { throw ALTError(.missingInfoPlist) }
let openAppURL = URL(string: "altstore-" + application.bundleIdentifier + "://")!
var allURLSchemes = infoDictionary[Bundle.Info.urlTypes] as? [[String: Any]] ?? []
// Embed open URL so AltBackup can return to AltStore.
let altstoreURLScheme = ["CFBundleTypeRole": "Editor",
"CFBundleURLName": application.bundleIdentifier,
"CFBundleURLSchemes": [openAppURL.scheme!]] as [String : Any]
allURLSchemes.append(altstoreURLScheme)
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes]
if application.isAltStoreApp
{
additionalValues[Bundle.Info.deviceID] = device.identifier
additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID
if
let machineIdentifier = certificate.machineIdentifier,
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
{
additionalValues[Bundle.Info.certificateID] = certificate.serialNumber
let certificateURL = application.fileURL.appendingPathComponent("ALTCertificate.p12")
try encryptedData.write(to: certificateURL, options: .atomic)
}
}
else if infoDictionary.keys.contains(Bundle.Info.deviceID)
{
// There is an ALTDeviceID entry, so assume the app is using AltKit and replace it with the device's UDID.
additionalValues[Bundle.Info.deviceID] = device.identifier
additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID
}
try prepare(appBundle, additionalInfoDictionaryValues: additionalValues)
for appExtension in application.appExtensions
{
guard let bundle = Bundle(url: appExtension.fileURL) else { throw ALTError(.missingAppBundle) }
try prepare(bundle)
}
let resigner = ALTSigner(team: team, certificate: certificate)
resigner.signApp(at: application.fileURL, provisioningProfiles: Array(profiles.values)) { (success, error) in
do
{
try Result(success, error).get()
let activeProfiles: Set<String>? = (team.type == .free && application.isAltStoreApp) ? Set(profiles.values.map(\.bundleIdentifier)) : nil
ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier, activeProvisioningProfiles: activeProfiles) { (success, error) in
completionHandler(Result(success, error))
}
}
catch
{
print("Failed to install app", error)
completionHandler(.failure(error))
}
}
}
catch
{
print("Failed to install AltStore", error)
completionHandler(.failure(error))
}
}
}
func showInstallationAlert(appName: String, deviceName: String)
{
let content = UNMutableNotificationContent()
content.title = String(format: NSLocalizedString("Installing %@ to %@...", comment: ""), appName, deviceName)
content.body = NSLocalizedString("This may take a few seconds.", comment: "")
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
}
}
private var securityCodeAlertKey = 0
private var securityCodeTextFieldKey = 0
extension ALTDeviceManager: NSTextFieldDelegate
{
var securityCodeAlert: NSAlert? {
get { return objc_getAssociatedObject(self, &securityCodeAlertKey) as? NSAlert }
set { objc_setAssociatedObject(self, &securityCodeAlertKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
var securityCodeTextField: NSTextField? {
get { return objc_getAssociatedObject(self, &securityCodeTextFieldKey) as? NSTextField }
set { objc_setAssociatedObject(self, &securityCodeTextFieldKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
public func controlTextDidChange(_ obj: Notification)
{
self.validate()
}
public func controlTextDidEndEditing(_ obj: Notification)
{
self.validate()
}
private func validate()
{
guard let code = self.securityCodeTextField?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) else { return }
if code.count == 6
{
self.securityCodeAlert?.buttons.first?.isEnabled = true
}
else
{
self.securityCodeAlert?.buttons.first?.isEnabled = false
}
self.securityCodeAlert?.layout()
}
}

View File

@@ -1,56 +0,0 @@
//
// ALTDeviceManager.h
// AltServer
//
// Created by Riley Testut on 5/24/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "AltSign.h"
@class ALTWiredConnection;
@class ALTNotificationConnection;
@class ALTDebugConnection;
@class ALTInstalledApp;
NS_ASSUME_NONNULL_BEGIN
extern NSNotificationName const ALTDeviceManagerDeviceDidConnectNotification NS_SWIFT_NAME(deviceManagerDeviceDidConnect);
extern NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification NS_SWIFT_NAME(deviceManagerDeviceDidDisconnect);
@interface ALTDeviceManager : NSObject
@property (class, nonatomic, readonly) ALTDeviceManager *sharedManager;
@property (nonatomic, readonly) NSArray<ALTDevice *> *connectedDevices;
@property (nonatomic, readonly) NSArray<ALTDevice *> *availableDevices;
- (void)start;
/* App Installation */
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)removeAppForBundleIdentifier:(NSString *)bundleIdentifier fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
/* Provisioning Profiles */
- (void)installProvisioningProfiles:(NSSet<ALTProvisioningProfile *> *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
- (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet<NSString *> *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
/* Developer Disk Image */
- (void)isDeveloperDiskImageMountedForDevice:(ALTDevice *)device
completionHandler:(void (^)(BOOL isMounted, NSError *_Nullable error))completionHandler;
- (void)installDeveloperDiskImageAtURL:(NSURL *)diskURL signatureURL:(NSURL *)signatureURL toDevice:(ALTDevice *)device
completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
/* Apps */
- (void)fetchInstalledAppsOnDevice:(ALTDevice *)altDevice completionHandler:(void (^)(NSSet<ALTInstalledApp *> *_Nullable installedApps, NSError *_Nullable error))completionHandler;
/* Connections */
- (void)startWiredConnectionToDevice:(ALTDevice *)device completionHandler:(void (^)(ALTWiredConnection *_Nullable connection, NSError *_Nullable error))completionHandler;
- (void)startNotificationConnectionToDevice:(ALTDevice *)device completionHandler:(void (^)(ALTNotificationConnection *_Nullable connection, NSError *_Nullable error))completionHandler;
- (void)startDebugConnectionToDevice:(ALTDevice *)device completionHandler:(void (^)(ALTDebugConnection *_Nullable connection, NSError * _Nullable error))completionHandler;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
//
// FileManager+URLs.swift
// AltServer
//
// Created by Riley Testut on 2/23/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
import Foundation
extension FileManager
{
var altserverDirectory: URL {
let applicationSupportDirectoryURL = self.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
let altserverDirectoryURL = applicationSupportDirectoryURL.appendingPathComponent("com.rileytestut.AltServer")
return altserverDirectoryURL
}
var certificatesDirectory: URL {
let certificatesDirectoryURL = self.altserverDirectory.appendingPathComponent("Certificates")
return certificatesDirectoryURL
}
var developerDisksDirectory: URL {
let developerDisksDirectoryURL = self.altserverDirectory.appendingPathComponent("DeveloperDiskImages")
return developerDisksDirectoryURL
}
}

View File

@@ -1,38 +0,0 @@
//
// UserDefaults+AltServer.swift
// AltServer
//
// Created by Riley Testut on 7/31/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
extension UserDefaults
{
var serverID: String? {
get {
return self.string(forKey: "serverID")
}
set {
self.set(newValue, forKey: "serverID")
}
}
var didPresentInitialNotification: Bool {
get {
return self.bool(forKey: "didPresentInitialNotification")
}
set {
self.set(newValue, forKey: "didPresentInitialNotification")
}
}
func registerDefaults()
{
if self.serverID == nil
{
self.serverID = UUID().uuidString
}
}
}

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Riley Testut. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>SUFeedURL</key>
<string>https://altstore.io/altserver/sparkle-macos.xml</string>
</dict>
</plist>

View File

@@ -1,29 +0,0 @@
//
// InstalledApp.swift
// AltServer
//
// Created by Riley Testut on 5/25/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
import Foundation
@objc(ALTInstalledApp) @objcMembers
class InstalledApp: NSObject, MenuDisplayable
{
let name: String
let bundleIdentifier: String
let executableName: String
init?(dictionary: [String: Any])
{
guard let name = dictionary[kCFBundleNameKey as String] as? String,
let bundleIdentifier = dictionary[kCFBundleIdentifierKey as String] as? String,
let executableName = dictionary[kCFBundleExecutableKey as String] as? String
else { return nil }
self.name = name
self.bundleIdentifier = bundleIdentifier
self.executableName = executableName
}
}

View File

@@ -1,115 +0,0 @@
//
// MenuController.swift
// AltServer
//
// Created by Riley Testut on 3/3/21.
// Copyright © 2021 Riley Testut. All rights reserved.
//
import Foundation
import AppKit
protocol MenuDisplayable
{
var name: String { get }
}
class MenuController<T: MenuDisplayable & Hashable>: NSObject, NSMenuDelegate
{
let menu: NSMenu
var items: [T] {
didSet {
self.submenus.removeAll()
self.updateMenu()
}
}
var placeholder: String? {
didSet {
self.updateMenu()
}
}
var action: ((T) -> Void)?
var submenuHandler: ((T) -> NSMenu)?
private var submenus = [T: NSMenu]()
init(menu: NSMenu, items: [T])
{
self.menu = menu
self.items = items
super.init()
self.menu.delegate = self
}
@objc
private func performAction(_ menuItem: NSMenuItem)
{
guard case let index = self.menu.index(of: menuItem), index != -1 else { return }
let item = self.items[index]
self.action?(item)
}
@objc
func numberOfItems(in menu: NSMenu) -> Int
{
let numberOfItems = (self.items.isEmpty && self.placeholder != nil) ? 1 : self.items.count
return numberOfItems
}
@objc
func menu(_ menu: NSMenu, update menuItem: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool
{
if let text = self.placeholder, self.items.isEmpty
{
menuItem.title = text
menuItem.isEnabled = false
menuItem.target = nil
menuItem.action = nil
}
else
{
let item = self.items[index]
menuItem.title = item.name
menuItem.isEnabled = true
menuItem.target = self
menuItem.action = #selector(MenuController.performAction(_:))
menuItem.tag = index
if let submenu = self.submenus[item] ?? self.submenuHandler?(item)
{
menuItem.submenu = submenu
// Cache submenu to prevent duplicate calls to submenuHandler.
self.submenus[item] = submenu
}
}
return true
}
}
private extension MenuController
{
func updateMenu()
{
self.menu.removeAllItems()
let numberOfItems = self.numberOfItems(in: self.menu)
for index in 0 ..< numberOfItems
{
let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "")
guard self.menu(self.menu, update: menuItem, at: index, shouldCancel: false) else { break }
self.menu.addItem(menuItem)
}
self.menu.update()
}
}

View File

@@ -1,372 +0,0 @@
//
// PluginManager.swift
// AltServer
//
// Created by Riley Testut on 9/16/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
import AppKit
import CryptoKit
import STPrivilegedTask
private let pluginDirectoryURL = URL(fileURLWithPath: "/Library/Mail/Bundles", isDirectory: true)
private let pluginURL = pluginDirectoryURL.appendingPathComponent("AltPlugin.mailbundle")
enum PluginError: LocalizedError
{
case cancelled
case unknown
case notFound
case mismatchedHash(hash: String, expectedHash: String)
case taskError(String)
case taskErrorCode(Int)
var errorDescription: String? {
switch self
{
case .cancelled: return NSLocalizedString("Mail plug-in installation was cancelled.", comment: "")
case .unknown: return NSLocalizedString("Failed to install Mail plug-in.", comment: "")
case .notFound: return NSLocalizedString("The Mail plug-in does not exist at the requested URL.", comment: "")
case .mismatchedHash(let hash, let expectedHash): return String(format: NSLocalizedString("The hash of the downloaded Mail plug-in does not match the expected hash.\n\nHash:\n%@\n\nExpected Hash:\n%@", comment: ""), hash, expectedHash)
case .taskError(let output): return output
case .taskErrorCode(let errorCode): return String(format: NSLocalizedString("There was an error installing the Mail plug-in. (Error Code: %@)", comment: ""), NSNumber(value: errorCode))
}
}
}
private extension URL
{
#if STAGING
static let altPluginUpdateURL = URL(string: "https://f000.backblazeb2.com/file/altstore-staging/altserver/altplugin/altplugin.json")!
#else
static let altPluginUpdateURL = URL(string: "https://cdn.altstore.io/file/altstore/altserver/altplugin/altplugin.json")!
#endif
}
class PluginManager
{
private let session = URLSession(configuration: .ephemeral)
private var latestPluginVersion: PluginVersion?
var isMailPluginInstalled: Bool {
let isMailPluginInstalled = FileManager.default.fileExists(atPath: pluginURL.path)
return isMailPluginInstalled
}
func isUpdateAvailable(completionHandler: @escaping (Result<Bool, Error>) -> Void)
{
self.isUpdateAvailable(useCache: false, completionHandler: completionHandler)
}
private func isUpdateAvailable(useCache: Bool, completionHandler: @escaping (Result<Bool, Error>) -> Void)
{
do
{
// If Mail plug-in is not yet installed, then there is no update available.
var isDirectory: ObjCBool = false
guard FileManager.default.fileExists(atPath: pluginURL.path, isDirectory: &isDirectory), isDirectory.boolValue else { return completionHandler(.success(false)) }
// Load Info.plist from disk because Bundle.infoDictionary is cached by system.
let infoDictionaryURL = pluginURL.appendingPathComponent("Contents/Info.plist")
guard let infoDictionary = NSDictionary(contentsOf: infoDictionaryURL) as? [String: Any],
let localVersion = infoDictionary["CFBundleShortVersionString"] as? String
else { throw CocoaError(.fileReadCorruptFile, userInfo: [NSURLErrorKey: infoDictionaryURL]) }
if let pluginVersion = self.latestPluginVersion, useCache
{
let isUpdateAvailable = (localVersion != pluginVersion.version)
completionHandler(.success(isUpdateAvailable))
}
else
{
self.fetchLatestPluginVersion(useCache: useCache) { result in
switch result
{
case .failure(let error): completionHandler(.failure(error))
case .success(let pluginVersion):
let isUpdateAvailable = (localVersion != pluginVersion.version)
completionHandler(.success(isUpdateAvailable))
}
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
extension PluginManager
{
func installMailPlugin(completionHandler: @escaping (Result<Void, Error>) -> Void)
{
self.isUpdateAvailable(useCache: true) { result in
DispatchQueue.main.async {
do
{
let isUpdateAvailable = try result.get()
let alert = NSAlert()
if isUpdateAvailable
{
alert.messageText = NSLocalizedString("Update Mail Plug-in", comment: "")
alert.informativeText = NSLocalizedString("An update is available for AltServer's Mail plug-in. Please update the plug-in now in order to keep using AltStore.", comment: "")
alert.addButton(withTitle: NSLocalizedString("Update Plug-in", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
}
else
{
alert.messageText = NSLocalizedString("Install Mail Plug-in", comment: "")
alert.informativeText = NSLocalizedString("AltServer requires a Mail plug-in in order to retrieve necessary information about your Apple ID. Would you like to install it now?", comment: "")
alert.addButton(withTitle: NSLocalizedString("Install Plug-in", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
}
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
let response = alert.runModal()
guard response == .alertFirstButtonReturn else { throw PluginError.cancelled }
self.downloadPlugin { (result) in
do
{
let fileURL = try result.get()
// Ensure plug-in directory exists.
let authorization = try self.runAndKeepAuthorization("mkdir", arguments: ["-p", pluginDirectoryURL.path])
// Create temporary directory.
let temporaryDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: temporaryDirectoryURL, withIntermediateDirectories: true, attributes: nil)
defer { try? FileManager.default.removeItem(at: temporaryDirectoryURL) }
// Unzip AltPlugin to temporary directory.
try self.runAndKeepAuthorization("unzip", arguments: ["-o", fileURL.path, "-d", temporaryDirectoryURL.path], authorization: authorization)
if FileManager.default.fileExists(atPath: pluginURL.path)
{
// Delete existing Mail plug-in.
try self.runAndKeepAuthorization("rm", arguments: ["-rf", pluginURL.path], authorization: authorization)
}
// Copy AltPlugin to Mail plug-ins directory.
// Must be separate step than unzip to prevent macOS from considering plug-in corrupted.
let unzippedPluginURL = temporaryDirectoryURL.appendingPathComponent(pluginURL.lastPathComponent)
try self.runAndKeepAuthorization("cp", arguments: ["-R", unzippedPluginURL.path, pluginDirectoryURL.path], authorization: authorization)
guard self.isMailPluginInstalled else { throw PluginError.unknown }
// Enable Mail plug-in preferences.
try self.run("defaults", arguments: ["write", "/Library/Preferences/com.apple.mail", "EnableBundles", "-bool", "YES"], authorization: authorization)
print("Finished installing Mail plug-in!")
completionHandler(.success(()))
}
catch
{
completionHandler(.failure(error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
}
func uninstallMailPlugin(completionHandler: @escaping (Result<Void, Error>) -> Void)
{
let alert = NSAlert()
alert.messageText = NSLocalizedString("Uninstall Mail Plug-in", comment: "")
alert.informativeText = NSLocalizedString("Are you sure you want to uninstall the AltServer Mail plug-in? You will no longer be able to install or refresh apps with AltStore.", comment: "")
alert.addButton(withTitle: NSLocalizedString("Uninstall Plug-in", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
let response = alert.runModal()
guard response == .alertFirstButtonReturn else { return completionHandler(.failure(PluginError.cancelled)) }
DispatchQueue.global().async {
do
{
if FileManager.default.fileExists(atPath: pluginURL.path)
{
// Delete Mail plug-in from privileged directory.
try self.run("rm", arguments: ["-rf", pluginURL.path])
}
completionHandler(.success(()))
}
catch
{
completionHandler(.failure(error))
}
}
}
}
private extension PluginManager
{
func fetchLatestPluginVersion(useCache: Bool, completionHandler: @escaping (Result<PluginVersion, Error>) -> Void)
{
if let pluginVersion = self.latestPluginVersion, useCache
{
return completionHandler(.success(pluginVersion))
}
guard #available(macOS 11, *) else {
// macOS versions prior to 11.0 require Mail plug-ins be *unsigned*,
// so we hardcode these versions to use the unsigned AltPlugin v1.0.
return completionHandler(.success(.v1_0))
}
let dataTask = self.session.dataTask(with: .altPluginUpdateURL) { (data, response, error) in
do
{
if let response = response as? HTTPURLResponse
{
guard response.statusCode != 404 else { return completionHandler(.failure(PluginError.notFound)) }
}
guard let data = data else { throw error! }
let response = try JSONDecoder().decode(PluginVersionResponse.self, from: data)
completionHandler(.success(response.pluginVersion))
}
catch
{
completionHandler(.failure(error))
}
}
dataTask.resume()
}
func downloadPlugin(completion: @escaping (Result<URL, Error>) -> Void)
{
self.fetchLatestPluginVersion(useCache: true) { result in
switch result
{
case .failure(let error): completion(.failure(error))
case .success(let pluginVersion):
func finish(_ result: Result<URL, Error>)
{
do
{
let fileURL = try result.get()
if #available(OSX 10.15, *)
{
let data = try Data(contentsOf: fileURL)
let sha256Hash = SHA256.hash(data: data)
let hashString = sha256Hash.compactMap { String(format: "%02x", $0) }.joined()
print("Comparing Mail plug-in hash (\(hashString)) against expected hash (\(pluginVersion.sha256Hash))...")
guard hashString == pluginVersion.sha256Hash else { throw PluginError.mismatchedHash(hash: hashString, expectedHash: pluginVersion.sha256Hash) }
}
completion(.success(fileURL))
}
catch
{
completion(.failure(error))
}
}
if pluginVersion.url.isFileURL
{
finish(.success(pluginVersion.url))
}
else
{
let downloadTask = URLSession.shared.downloadTask(with: pluginVersion.url) { (fileURL, response, error) in
if let response = response as? HTTPURLResponse
{
guard response.statusCode != 404 else { return finish(.failure(PluginError.notFound)) }
}
let result = Result(fileURL, error)
finish(result)
if let fileURL = fileURL
{
try? FileManager.default.removeItem(at: fileURL)
}
}
downloadTask.resume()
}
}
}
}
func run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws
{
_ = try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: true)
}
@discardableResult
func runAndKeepAuthorization(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil) throws -> AuthorizationRef
{
return try self._run(program, arguments: arguments, authorization: authorization, freeAuthorization: false)
}
func _run(_ program: String, arguments: [String], authorization: AuthorizationRef? = nil, freeAuthorization: Bool) throws -> AuthorizationRef
{
var launchPath = "/usr/bin/" + program
if !FileManager.default.fileExists(atPath: launchPath)
{
launchPath = "/bin/" + program
}
print("Running program:", launchPath)
let task = STPrivilegedTask()
task.launchPath = launchPath
task.arguments = arguments
task.freeAuthorizationWhenDone = freeAuthorization
let errorCode: OSStatus
if let authorization = authorization
{
errorCode = task.launch(withAuthorization: authorization)
}
else
{
errorCode = task.launch()
}
guard errorCode == 0 else { throw PluginError.taskErrorCode(Int(errorCode)) }
task.waitUntilExit()
print("Exit code:", task.terminationStatus)
guard task.terminationStatus == 0 else {
let outputData = task.outputFileHandle.readDataToEndOfFile()
if let outputString = String(data: outputData, encoding: .utf8), !outputString.isEmpty
{
throw PluginError.taskError(outputString)
}
throw PluginError.taskErrorCode(Int(task.terminationStatus))
}
guard let authorization = task.authorization else { throw PluginError.unknown }
return authorization
}
}

View File

@@ -1,37 +0,0 @@
//
// PluginVersion.swift
// AltServer
//
// Created by Riley Testut and Weedles on 2/15/22 <3
// Copyright © 2022 Riley Testut. All rights reserved.
//
import Foundation
struct PluginVersion: Decodable
{
var url: URL
var sha256Hash: String
var version: String
static let v1_0 = PluginVersion(url: URL(string: "https://f000.backblazeb2.com/file/altstore/altserver/altplugin/1_0.zip")!,
sha256Hash: "070e9b7e1f74e7a6474d36253ab5a3623ff93892acc9e1043c3581f2ded12200",
version: "1.0")
static let v1_9 = PluginVersion(url: Bundle.main.url(forResource: "AltPlugin", withExtension: "zip")!,
sha256Hash: "83ead26d8776ef6850e06fe3d1c5c5559aca284718b1cf3cc49785ba6b1e2849",
version: "1.9")
private enum CodingKeys: String, CodingKey
{
case url
case sha256Hash = "sha256"
case version
}
}
struct PluginVersionResponse: Decodable
{
var version: Int
var pluginVersion: PluginVersion
}

3
AltStore.xcconfig Normal file
View File

@@ -0,0 +1,3 @@
#include "Build.xcconfig"
PRODUCT_BUNDLE_IDENTIFIER = $(PRODUCT_BUNDLE_IDENTIFIER)

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:AltStore.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,167 @@
{
"pins" : [
{
"identity" : "altsign",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SideStore/AltSign",
"state" : {
"branch" : "master",
"revision" : "7e0e7edcf8fbc44ac1e35da3e9030a297aa18b84"
}
},
{
"identity" : "appcenter-sdk-apple",
"kind" : "remoteSourceControl",
"location" : "https://github.com/microsoft/appcenter-sdk-apple.git",
"state" : {
"revision" : "8354a50fe01a7e54e196d3b5493b5ab53dd5866a",
"version" : "4.4.2"
}
},
{
"identity" : "asyncimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/fabianthdev/AsyncImage",
"state" : {
"branch" : "main",
"revision" : "018a4fffea025066d795ebb025c2769183f3fffb"
}
},
{
"identity" : "expandabletext",
"kind" : "remoteSourceControl",
"location" : "https://github.com/fabianthdev/ExpandableText",
"state" : {
"branch" : "main",
"revision" : "a375f5b8c73f0af69aa7add890378fdf404a29bc"
}
},
{
"identity" : "inject",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzysztofzablocki/Inject.git",
"state" : {
"revision" : "abcc4b091fd384cfd09b149a60298b75dc87c5b9",
"version" : "1.2.3"
}
},
{
"identity" : "keychainaccess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
"state" : {
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
"version" : "4.2.2"
}
},
{
"identity" : "launchatlogin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/sindresorhus/LaunchAtLogin.git",
"state" : {
"revision" : "e8171b3e38a2816f579f58f3dac1522aa39efe41",
"version" : "4.2.0"
}
},
{
"identity" : "localconsole",
"kind" : "remoteSourceControl",
"location" : "https://github.com/naturecodevoid/LocalConsole.git",
"state" : {
"branch" : "main",
"revision" : "4ead9c3e565190172caac62b5179347e02999365"
}
},
{
"identity" : "nuke",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kean/Nuke.git",
"state" : {
"revision" : "9318d02a8a6d20af56505c9673261c1fd3b3aebe",
"version" : "7.6.3"
}
},
{
"identity" : "openssl",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/OpenSSL",
"state" : {
"revision" : "033fcb41dac96b1b6effa945ca1f9ade002370b2",
"version" : "1.1.1501"
}
},
{
"identity" : "plcrashreporter",
"kind" : "remoteSourceControl",
"location" : "https://github.com/microsoft/PLCrashReporter.git",
"state" : {
"revision" : "6b27393cad517c067dceea85fadf050e70c4ceaa",
"version" : "1.10.1"
}
},
{
"identity" : "reachability.swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ashleymills/Reachability.swift",
"state" : {
"branch" : "master",
"revision" : "a81b7367f2c46875f29577e03a60c39cdfad0c8d"
}
},
{
"identity" : "semanticversion",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SwiftPackageIndex/SemanticVersion.git",
"state" : {
"revision" : "fc670910dc0903cc269b3d0b776cda5703979c4e",
"version" : "0.3.5"
}
},
{
"identity" : "sfsafesymbols",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SFSafeSymbols/SFSafeSymbols",
"state" : {
"revision" : "50bc33264e6c0972f905b61af656201cf6091de8",
"version" : "4.0.0"
}
},
{
"identity" : "sparkle",
"kind" : "remoteSourceControl",
"location" : "https://github.com/sparkle-project/Sparkle.git",
"state" : {
"revision" : "286edd1fa22505a9e54d170e9fd07d775ea233f2",
"version" : "2.1.0"
}
},
{
"identity" : "starscream",
"kind" : "remoteSourceControl",
"location" : "https://github.com/daltoniam/Starscream.git",
"state" : {
"revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21",
"version" : "4.0.4"
}
},
{
"identity" : "stprivilegedtask",
"kind" : "remoteSourceControl",
"location" : "https://github.com/JoeMatt/STPrivilegedTask.git",
"state" : {
"branch" : "master",
"revision" : "10a9150ef32d444af326beba76356ae9af95a3e7"
}
},
{
"identity" : "zipfoundation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/weichsel/ZIPFoundation.git",
"state" : {
"revision" : "43ec568034b3731101dbf7670765d671c30f54f3",
"version" : "0.9.16"
}
}
],
"version" : 2
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1030"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,16 +14,16 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7083360F3F274C756CA77375F9D2A2BD"
BuildableName = "Pods_AltStore.framework"
BlueprintName = "Pods-AltStore"
ReferencedContainer = "container:Pods.xcodeproj">
BlueprintIdentifier = "BFD247692284B9A500981D42"
BuildableName = "SideStore.app"
BlueprintName = "SideStore"
ReferencedContainer = "container:AltStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
@@ -31,7 +31,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
@@ -40,15 +40,22 @@
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7083360F3F274C756CA77375F9D2A2BD"
BuildableName = "Pods_AltStore.framework"
BlueprintName = "Pods-AltStore"
ReferencedContainer = "container:Pods.xcodeproj">
BlueprintIdentifier = "BFD247692284B9A500981D42"
BuildableName = "SideStore.app"
BlueprintName = "SideStore"
ReferencedContainer = "container:AltStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-com.apple.CoreData.ConcurrencyDebug 1"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -56,18 +63,19 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7083360F3F274C756CA77375F9D2A2BD"
BuildableName = "Pods_AltStore.framework"
BlueprintName = "Pods-AltStore"
ReferencedContainer = "container:Pods.xcodeproj">
BlueprintIdentifier = "BFD247692284B9A500981D42"
BuildableName = "SideStore.app"
BlueprintName = "SideStore"
ReferencedContainer = "container:AltStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
buildConfiguration = "Release">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"

View File

@@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFD247692284B9A500981D42"
BuildableName = "AltStore.app"
BlueprintName = "AltStore"
BuildableName = "SideStore.app"
BlueprintName = "SideStore"
ReferencedContainer = "container:AltStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -45,8 +45,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFD247692284B9A500981D42"
BuildableName = "AltStore.app"
BlueprintName = "AltStore"
BuildableName = "SideStore.app"
BlueprintName = "SideStore"
ReferencedContainer = "container:AltStore.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
@@ -68,8 +68,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFD247692284B9A500981D42"
BuildableName = "AltStore.app"
BlueprintName = "AltStore"
BuildableName = "SideStore.app"
BlueprintName = "SideStore"
ReferencedContainer = "container:AltStore.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "container:AltStore.xcodeproj">
</FileRef>
<FileRef
location = "group:Dependencies/AltSign">
</FileRef>
<FileRef
location = "group:Dependencies/Roxas/Roxas.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -1,16 +0,0 @@
{
"object": {
"pins": [
{
"package": "LaunchAtLogin",
"repositoryURL": "https://github.com/sindresorhus/LaunchAtLogin.git",
"state": {
"branch": null,
"revision": "6b16bcdf7d45a9d76a768a5c4912dde925cf0e95",
"version": "4.1.0"
}
}
]
},
"version": 1
}

View File

@@ -6,3 +6,7 @@
#import "ALTAppPatcher.h"
#include "fragmentzip.h"
#ifdef MDC
#import "grant_full_disk_access.h"
#endif /* MDC */

View File

@@ -8,7 +8,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.rileytestut.AltStore</string>
<string>group.$(APP_GROUP_IDENTIFIER)</string>
</array>
</dict>
</plist>

View File

@@ -14,13 +14,7 @@ import AppCenter
import AppCenterAnalytics
import AppCenterCrashes
#if DEBUG
private let appCenterAppSecret = "bb08e9bb-c126-408d-bf3f-324c8473fd40"
#elseif RELEASE
private let appCenterAppSecret = "b6718932-294a-432b-81f2-be1e17ff85c5"
#else
private let appCenterAppSecret = "e873f6ca-75eb-4685-818f-801e0e375d60"
#endif
private let appCenterAppSecret = "73532d3e-e573-4693-99a4-9f85840bbb44"
extension AnalyticsManager
{
@@ -77,7 +71,7 @@ extension AnalyticsManager
}
}
class AnalyticsManager
final class AnalyticsManager
{
static let shared = AnalyticsManager()

View File

@@ -10,18 +10,20 @@ import UIKit
import UserNotifications
import AVFoundation
import Intents
import LocalConsole
import AltStoreCore
import AltSign
import Roxas
import EmotionalDamage
extension AppDelegate
{
static let openPatreonSettingsDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.OpenPatreonSettingsDeepLinkNotification")
static let importAppDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.ImportAppDeepLinkNotification")
static let addSourceDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.AddSourceDeepLinkNotification")
static let openPatreonSettingsDeepLinkNotification = Notification.Name(Bundle.Info.appbundleIdentifier + ".OpenPatreonSettingsDeepLinkNotification")
static let importAppDeepLinkNotification = Notification.Name(Bundle.Info.appbundleIdentifier + ".ImportAppDeepLinkNotification")
static let addSourceDeepLinkNotification = Notification.Name(Bundle.Info.appbundleIdentifier + ".AddSourceDeepLinkNotification")
static let appBackupDidFinish = Notification.Name("com.rileytestut.AltStore.AppBackupDidFinish")
static let appBackupDidFinish = Notification.Name(Bundle.Info.appbundleIdentifier + ".AppBackupDidFinish")
static let importAppDeepLinkURLKey = "fileURL"
static let appBackupResultKey = "result"
@@ -29,7 +31,7 @@ extension AppDelegate
}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
final class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
@@ -57,9 +59,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
// Copy STDOUT and STDERR to the logging console
_ = OutputCapturer.shared
// Register default settings before doing anything else.
UserDefaults.registerDefaults()
#if UNSTABLE
UnstableFeatures.load()
#endif
LCManager.shared.isVisible = UserDefaults.standard.isConsoleEnabled
LCManager.shared.isCharacterLimitDisabled = true // we want all logs exported
DatabaseManager.shared.start { (error) in
if let error = error
{
@@ -75,8 +87,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
self.setTintColor()
ServerManager.shared.startDiscovering()
SecureValueTransformer.register()
if UserDefaults.standard.firstLaunch == nil
@@ -98,15 +108,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidEnterBackground(_ application: UIApplication)
{
ServerManager.shared.stopDiscovering()
// Make sure to update SceneDelegate.sceneDidEnterBackground() as well.
guard let oneMonthAgo = Calendar.current.date(byAdding: .month, value: -1, to: Date()) else { return }
let midnightOneMonthAgo = Calendar.current.startOfDay(for: oneMonthAgo)
DatabaseManager.shared.purgeLoggedErrors(before: midnightOneMonthAgo) { result in
switch result
{
case .success: break
case .failure(let error): print("[ALTLog] Failed to purge logged errors before \(midnightOneMonthAgo).", error)
}
}
}
func applicationWillEnterForeground(_ application: UIApplication)
{
AppManager.shared.update()
ServerManager.shared.startDiscovering()
PatreonAPI.shared.refreshPatreonAccount()
start_em_proxy(bind_addr: Consts.Proxy.serverURL)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool
@@ -267,7 +287,7 @@ extension AppDelegate
let content = UNMutableNotificationContent()
content.title = NSLocalizedString("App Refresh Tip", comment: "")
content.body = NSLocalizedString("The more you open AltStore, the more chances it's given to refresh apps in the background.", comment: "")
content.body = NSLocalizedString("The more you open SideStore, the more chances it's given to refresh apps in the background.", comment: "")
let request = UNNotificationRequest(identifier: "background-refresh-reminder5", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
@@ -371,11 +391,11 @@ private extension AppDelegate
for update in updates
{
guard !previousUpdates.contains(where: { $0[#keyPath(InstalledApp.bundleIdentifier)] == update.bundleIdentifier }) else { continue }
guard let storeApp = update.storeApp else { continue }
guard let storeApp = update.storeApp, let version = storeApp.version else { continue }
let content = UNMutableNotificationContent()
content.title = NSLocalizedString("New Update Available", comment: "")
content.body = String(format: NSLocalizedString("%@ %@ is now available for download.", comment: ""), update.name, storeApp.version)
content.body = String(format: NSLocalizedString("%@ %@ is now available for download.", comment: ""), update.name, version)
content.sound = .default
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
@@ -395,7 +415,7 @@ private extension AppDelegate
}
else
{
content.title = NSLocalizedString("AltStore News", comment: "")
content.title = NSLocalizedString("SideStore News", comment: "")
}
content.body = newsItem.title

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="wKh-xq-NuP">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21223" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="wKh-xq-NuP">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21204"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
@@ -13,7 +13,7 @@
<!--Launch View Controller-->
<scene sceneID="q24-yd-v7v">
<objects>
<viewController id="wKh-xq-NuP" customClass="LaunchViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="wKh-xq-NuP" customClass="LaunchViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="G9E-Qs-gFM">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -28,7 +28,7 @@
<!--Tab Bar Controller-->
<scene sceneID="yl2-sM-qoP">
<objects>
<tabBarController storyboardIdentifier="tabBarController" modalPresentationStyle="fullScreen" id="49e-Tb-3d3" customClass="TabBarController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<tabBarController storyboardIdentifier="tabBarController" modalPresentationStyle="fullScreen" id="49e-Tb-3d3" customClass="TabBarController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<tabBar key="tabBar" contentMode="scaleToFill" id="W28-zg-YXA">
<rect key="frame" x="0.0" y="975" width="768" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
@@ -50,7 +50,7 @@
<!--Browse-->
<scene sceneID="rXq-UR-qQp">
<objects>
<collectionViewController id="e3L-BF-iXp" customClass="BrowseViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionViewController id="e3L-BF-iXp" customClass="BrowseViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="CaT-1q-qnx">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -85,7 +85,7 @@
<!--App View Controller-->
<scene sceneID="TgT-LO-3Er">
<objects>
<viewController storyboardIdentifier="appViewController" id="0V6-N4-hTO" customClass="AppViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<viewController storyboardIdentifier="appViewController" id="0V6-N4-hTO" customClass="AppViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="0cR-li-tCB">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -93,7 +93,7 @@
<stackView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="Bql-t3-Ndi">
<rect key="frame" x="47" y="238" width="85" height="35"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="j1W-Jn-HFI" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target">
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="j1W-Jn-HFI" customClass="AppIconImageView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="35" height="35"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="aMS-N7-WYn"/>
@@ -131,7 +131,7 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Qlg-m3-lXg">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NEy-yr-cLS" customClass="AppBannerView" customModule="AltStore" customModuleProvider="target">
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NEy-yr-cLS" customClass="AppBannerView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="37" y="287" width="300" height="93"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</view>
@@ -192,7 +192,7 @@
</view>
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="maq-gT-QcS">
<barButtonItem key="rightBarButtonItem" style="plain" id="FLf-DS-F77">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="grk-xM-YWA" customClass="PillButton" customModule="AltStore" customModuleProvider="target">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="grk-xM-YWA" customClass="PillButton" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="287" y="6.5" width="72" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="0.22352941179999999" green="0.4941176471" blue="0.39607843139999999" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
@@ -229,7 +229,7 @@
<!--App-->
<scene sceneID="CgX-7h-sRI">
<objects>
<tableViewController id="kBq-V8-3XC" customClass="AppContentViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController id="kBq-V8-3XC" customClass="AppContentViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" contentInsetAdjustmentBehavior="never" dataMode="static" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" contentViewInsetsToSafeArea="NO" id="w5c-Q3-FcU">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -238,7 +238,7 @@
<tableViewSection id="rfR-32-T0h">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="57" id="xef-ko-Qp1">
<rect key="frame" x="0.0" y="28" width="375" height="57"/>
<rect key="frame" x="0.0" y="50" width="375" height="57"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xef-ko-Qp1" id="8PX-jQ-nHd">
<rect key="frame" x="0.0" y="0.0" width="375" height="57"/>
@@ -262,7 +262,7 @@
<inset key="separatorInset" minX="15" minY="0.0" maxX="15" maxY="0.0"/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="nI6-wC-H2d">
<rect key="frame" x="0.0" y="85" width="375" height="44"/>
<rect key="frame" x="0.0" y="107" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="nI6-wC-H2d" id="Z4y-vb-Z4Q">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
@@ -299,14 +299,14 @@
<color key="backgroundColor" name="Background"/>
<inset key="separatorInset" minX="15" minY="0.0" maxX="15" maxY="0.0"/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="EL5-UC-RIw" customClass="AppContentTableViewCell" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="129" width="375" height="44"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="EL5-UC-RIw" customClass="AppContentTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="151" width="375" height="98"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EL5-UC-RIw" id="D1G-nK-G0Z">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EL5-UC-RIw" id="D1G-nK-G0Z">
<rect key="frame" x="0.0" y="0.0" width="375" height="98"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Hello Me" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Pyt-8D-BZA" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target">
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Hello Me" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Pyt-8D-BZA" customClass="CollapsingTextView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="20" y="20" width="335" height="34"/>
<color key="backgroundColor" name="Background"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
@@ -323,30 +323,30 @@
<color key="backgroundColor" name="Background"/>
<inset key="separatorInset" minX="1000" minY="0.0" maxX="0.0" maxY="0.0"/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="47M-El-a4G" customClass="AppContentTableViewCell" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="173" width="375" height="44"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="47M-El-a4G" customClass="AppContentTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="249" width="375" height="137.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="47M-El-a4G" id="f9D-OR-oGE">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="137.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" axis="vertical" distribution="equalSpacing" spacing="12" translatesAutoresizingMaskIntoConstraints="NO" id="n9R-39-Glq">
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="137.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="y3w-4S-e64">
<rect key="frame" x="20" y="0.0" width="335" height="4"/>
<rect key="frame" x="20" y="0.0" width="335" height="47.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" spacing="11" translatesAutoresizingMaskIntoConstraints="NO" id="PFD-gZ-77F">
<rect key="frame" x="0.0" y="0.0" width="335" height="0.0"/>
<rect key="frame" x="0.0" y="0.0" width="335" height="26.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="What's New" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="obM-TM-y2E">
<rect key="frame" x="0.0" y="0.0" width="124" height="0.0"/>
<rect key="frame" x="0.0" y="0.0" width="124" height="26.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="2w ago" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wGD-mS-8fO">
<rect key="frame" x="285" y="0.0" width="50" height="0.0"/>
<rect key="frame" x="285" y="0.0" width="50" height="26.5"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
@@ -354,16 +354,16 @@
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="ewH-gi-pyW">
<rect key="frame" x="0.0" y="4" width="335" height="0.0"/>
<rect key="frame" x="0.0" y="30.5" width="335" height="17"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Version 4.4.2" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7E0-TV-G4l">
<rect key="frame" x="0.0" y="0.0" width="84.5" height="0.0"/>
<rect key="frame" x="0.0" y="0.0" width="84.5" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="50.4 MB" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DgM-bD-bBY">
<rect key="frame" x="280.5" y="0.0" width="54.5" height="0.0"/>
<rect key="frame" x="280.5" y="0.0" width="54.5" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
@@ -372,8 +372,8 @@
</stackView>
</subviews>
</stackView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="20" y="16" width="335" height="0.0"/>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingTextView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="20" y="59.5" width="335" height="34"/>
<color key="backgroundColor" name="Background"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@@ -393,7 +393,7 @@
<inset key="separatorInset" minX="1000" minY="0.0" maxX="0.0" maxY="0.0"/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="149" id="nM7-vJ-W8b">
<rect key="frame" x="0.0" y="217" width="375" height="149"/>
<rect key="frame" x="0.0" y="386.5" width="375" height="149"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="nM7-vJ-W8b" id="cQ2-Jd-pRK">
<rect key="frame" x="0.0" y="0.0" width="375" height="149"/>
@@ -421,15 +421,15 @@
<inset key="sectionInset" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="WYy-bZ-h3T" customClass="PermissionCollectionViewCell" customModule="AltStore" customModuleProvider="target">
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="WYy-bZ-h3T" customClass="PermissionCollectionViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="20" y="0.0" width="60" height="88"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="60" height="88"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" alignment="center" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="fSx-We-L4W">
<rect key="frame" x="0.0" y="0.0" width="56" height="56"/>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="fSx-We-L4W">
<rect key="frame" x="0.0" y="0.0" width="60" height="87.5"/>
<subviews>
<button opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="79g-9q-mE2">
<rect key="frame" x="5" y="0.0" width="50" height="50"/>
@@ -511,7 +511,7 @@ World</string>
<!--Permission Popover View Controller-->
<scene sceneID="24j-EJ-G4e">
<objects>
<viewController id="Ojq-DN-xcF" customClass="PermissionPopoverViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="Ojq-DN-xcF" customClass="PermissionPopoverViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="IgU-aM-YrX">
<rect key="frame" x="0.0" y="0.0" width="375" height="217"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -566,7 +566,7 @@ World</string>
<!--News-->
<scene sceneID="bqw-wB-hyB">
<objects>
<collectionViewController id="3sa-FZ-PTg" customClass="NewsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionViewController id="3sa-FZ-PTg" customClass="NewsViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" id="736-lq-Aef">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -592,10 +592,10 @@ World</string>
<!--Browse-->
<scene sceneID="VHa-uP-bFU">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="faz-B4-Sub" customClass="ForwardingNavigationController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="faz-B4-Sub" customClass="ForwardingNavigationController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Browse" image="Browse" id="Uwh-Bg-Ymq"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="dIv-qd-9L5" customClass="NavigationBar" customModule="AltStore" customModuleProvider="target">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="dIv-qd-9L5" customClass="NavigationBar" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
<color key="tintColor" name="Primary"/>
@@ -620,12 +620,12 @@ World</string>
<!--My Apps-->
<scene sceneID="nhh-BJ-XiT">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="3Ew-ox-i4n" customClass="ForwardingNavigationController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="3Ew-ox-i4n" customClass="ForwardingNavigationController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="My Apps" image="MyApps" id="4gT-9u-k7y">
<color key="badgeColor" name="Primary"/>
</tabBarItem>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="CzO-Kt-BlZ" customClass="NavigationBar" customModule="AltStore" customModuleProvider="target">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="CzO-Kt-BlZ" customClass="NavigationBar" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
@@ -641,7 +641,7 @@ World</string>
<!--My Apps-->
<scene sceneID="EC8-Sf-AF9">
<objects>
<collectionViewController id="hv7-Ar-voT" customClass="MyAppsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionViewController id="hv7-Ar-voT" customClass="MyAppsViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" id="Jrp-gi-4Df">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -653,14 +653,14 @@ World</string>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" reuseIdentifier="AppCell" id="kMp-ym-2yu" customClass="InstalledAppCollectionViewCell" customModule="AltStore" customModuleProvider="target">
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" reuseIdentifier="AppCell" id="kMp-ym-2yu" customClass="InstalledAppCollectionViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mos-e4-dQ7" customClass="AppBannerView" customModule="AltStore" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mos-e4-dQ7" customClass="AppBannerView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="8" y="0.0" width="359" height="60"/>
</view>
</subviews>
@@ -679,7 +679,7 @@ World</string>
</segue>
</connections>
</collectionViewCell>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="NoUpdatesCell" id="h0f-XI-UA5" customClass="NoUpdatesCollectionViewCell" customModule="AltStore" customModuleProvider="target">
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="NoUpdatesCell" id="h0f-XI-UA5" customClass="NoUpdatesCollectionViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="75" width="375" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
@@ -740,7 +740,7 @@ World</string>
</connections>
</collectionViewCell>
</cells>
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="InstalledAppsFooter" id="HYs-co-nJZ" customClass="InstalledAppsCollectionFooterView" customModule="AltStore" customModuleProvider="target">
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="InstalledAppsFooter" id="HYs-co-nJZ" customClass="InstalledAppsCollectionFooterView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="135" width="375" height="60.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
@@ -797,7 +797,7 @@ World</string>
<!--App IDs-->
<scene sceneID="kvf-US-rRe">
<objects>
<collectionViewController title="App IDs" id="y1A-Nm-mw7" customClass="AppIDsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionViewController title="App IDs" id="y1A-Nm-mw7" customClass="AppIDsViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" dataMode="prototypes" id="v1r-C8-h6h">
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -809,14 +809,14 @@ World</string>
<inset key="sectionInset" minX="0.0" minY="10" maxX="0.0" maxY="20"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" reuseIdentifier="Cell" id="XWu-DU-xbh" customClass="BannerCollectionViewCell" customModule="AltStore" customModuleProvider="target">
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" reuseIdentifier="Cell" id="XWu-DU-xbh" customClass="BannerCollectionViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="70" width="375" height="80"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="375" height="80"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1w8-fI-98T" customClass="AppBannerView" customModule="AltStore" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1w8-fI-98T" customClass="AppBannerView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="8" y="0.0" width="359" height="80"/>
<accessibility key="accessibilityConfiguration">
<bool key="isElement" value="YES"/>
@@ -835,7 +835,7 @@ World</string>
</connections>
</collectionViewCell>
</cells>
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="Header" id="th0-G6-bRt" customClass="TextCollectionReusableView" customModule="AltStore" customModuleProvider="target">
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="Header" id="th0-G6-bRt" customClass="TextCollectionReusableView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
@@ -856,7 +856,7 @@ World</string>
<outlet property="textLabel" destination="83Z-Ih-nOW" id="xxM-HD-iJS"/>
</connections>
</collectionReusableView>
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="Footer" id="xMh-lD-r6C" customClass="TextCollectionReusableView" customModule="AltStore" customModuleProvider="target">
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="Footer" id="xMh-lD-r6C" customClass="TextCollectionReusableView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="170" width="375" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
@@ -883,7 +883,7 @@ World</string>
<navigationItem key="navigationItem" title="App IDs" id="3Co-uv-Fhb">
<barButtonItem key="leftBarButtonItem" style="plain" id="Aqs-QK-Ups">
<view key="customView" contentMode="scaleToFill" id="p0q-Fg-3Ba">
<rect key="frame" x="16" y="7" width="83" height="42"/>
<rect key="frame" x="16" y="1" width="83" height="42"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</view>
</barButtonItem>
@@ -905,10 +905,10 @@ World</string>
<!--News-->
<scene sceneID="BV8-6J-nIv">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="kjR-gi-fgT" customClass="ForwardingNavigationController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="kjR-gi-fgT" customClass="ForwardingNavigationController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="News" image="News" id="fVN-ed-uO1"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="525-jF-uDK" customClass="NavigationBar" customModule="AltStore" customModuleProvider="target">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="525-jF-uDK" customClass="NavigationBar" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
<edgeInsets key="layoutMargins" top="8" left="20" bottom="8" right="8"/>
@@ -928,7 +928,7 @@ World</string>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="IXk-qg-mFJ" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="9sB-f3-Fnk">
<rect key="frame" x="0.0" y="0.0" width="375" height="108"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
@@ -943,7 +943,7 @@ World</string>
<!--Sources-->
<scene sceneID="0S1-zn-9KZ">
<objects>
<collectionViewController title="Sources" id="cHC-TX-KzQ" customClass="SourcesViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionViewController title="Sources" id="cHC-TX-KzQ" customClass="SourcesViewController" customModule="SideStore" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" alwaysBounceVertical="YES" dataMode="prototypes" id="S36-hD-vu2">
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -955,14 +955,14 @@ World</string>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" reuseIdentifier="Cell" id="XcN-o4-9qm" customClass="BannerCollectionViewCell" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="210" width="375" height="80"/>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" reuseIdentifier="Cell" id="XcN-o4-9qm" customClass="BannerCollectionViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="200" width="375" height="80"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="375" height="80"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LW1-CC-bWu" customClass="AppBannerView" customModule="AltStore" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LW1-CC-bWu" customClass="AppBannerView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="8" y="0.0" width="359" height="80"/>
<accessibility key="accessibilityConfiguration">
<bool key="isElement" value="YES"/>
@@ -981,11 +981,11 @@ World</string>
</connections>
</collectionViewCell>
</cells>
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="Header" id="8N7-JY-mcA" customClass="TextCollectionReusableView" customModule="AltStore" customModuleProvider="target">
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="Header" id="8N7-JY-mcA" customClass="TextCollectionReusableView" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="200"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Manage sources to control which apps are available to download through AltStore." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TZv-TM-uJj">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Manage sources to control which apps are available to download through SideStore." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TZv-TM-uJj">
<rect key="frame" x="8" y="14" width="359" height="171"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -1014,10 +1014,10 @@ World</string>
<rect key="frame" x="8" y="0.0" width="359" height="50"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="PNx-uR-y2F">
<rect key="frame" x="0.0" y="0.0" width="359" height="3"/>
<rect key="frame" x="0.0" y="0.0" width="359" height="0.0"/>
</activityIndicatorView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="800" scrollEnabled="NO" contentInsetAdjustmentBehavior="never" editable="NO" text="Test" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="66c-H8-KJx">
<rect key="frame" x="0.0" y="18" width="359" height="32"/>
<rect key="frame" x="0.0" y="15" width="359" height="35"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
@@ -1070,7 +1070,7 @@ World</string>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="Qo4-72-Hmr" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="mcx-oR-qPe">
<rect key="frame" x="0.0" y="0.0" width="375" height="108"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
@@ -1095,13 +1095,13 @@ World</string>
<image name="News" width="19" height="20"/>
<image name="Settings" width="20" height="20"/>
<namedColor name="Background">
<color red="0.0040000001899898052" green="0.50199997425079346" blue="0.51800000667572021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.6431" green="0.0196" blue="0.9804" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="BlurTint">
<color red="1" green="1" blue="1" alpha="0.30000001192092896" colorSpace="custom" customColorSpace="sRGB"/>
<color red="1" green="1" blue="1" alpha="0.3" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="Primary">
<color red="0.0040000001899898052" green="0.50199997425079346" blue="0.51800000667572021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.6431" green="0.0196" blue="0.9804" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>

View File

@@ -0,0 +1,17 @@
//
// Proxy.swift
// SideStore
//
// Created by Joseph Mattiello on 11/7/22.
// Copyright © 2022 Riley Testut. All rights reserved.
//
import Foundation
public extension Consts {
enum Proxy {
static let address = "127.0.0.1"
static let port = "51820"
static let serverURL = "\(address):\(port)"
}
}

View File

@@ -0,0 +1,13 @@
//
// Consts.swift
// SideStore
//
// Created by Joseph Mattiello on 11/7/22.
// Copyright © 2022 Riley Testut. All rights reserved.
//
import Foundation
public enum Consts {
}

View File

@@ -0,0 +1,13 @@
//
// Error+Message.swift
// SideStore
//
// Created by naturecodevoid on 5/30/23.
// Copyright © 2023 SideStore. All rights reserved.
//
extension Error {
func message() -> String {
(self as? LocalizedError)?.failureReason ?? self.localizedDescription
}
}

View File

@@ -0,0 +1,74 @@
//
// OSLog+SideStore.swift
// SideStore
//
// Created by Joseph Mattiello on 11/16/22.
// Copyright © 2022 SideStore. All rights reserved.
//
import Foundation
import OSLog
public let customLog = OSLog(subsystem: "org.sidestore.sidestore",
category: "ios")
public extension OSLog {
/// Error logger extension
/// - Parameters:
/// - message: String or format string
/// - args: optional args for format string
@inlinable
static func error(_ message: StaticString, _ args: CVarArg...) {
os_log(message, log: customLog, type: .error, args)
}
/// Info logger extension
/// - Parameters:
/// - message: String or format string
/// - args: optional args for format string
@inlinable
static func info(_ message: StaticString, _ args: CVarArg...) {
os_log(message, log: customLog, type: .info, args)
}
/// Debug logger extension
/// - Parameters:
/// - message: String or format string
/// - args: optional args for format string
@inlinable
static func debug(_ message: StaticString, _ args: CVarArg...) {
os_log(message, log: customLog, type: .debug, args)
}
}
// TODO: Add file,line,function to messages? -- @JoeMatt
/// Error logger convenience method for SideStore logging
/// - Parameters:
/// - message: String or format string
/// - args: optional args for format string
@inlinable
public func ELOG(_ message: StaticString, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, _ args: CVarArg...) {
OSLog.error(message, args)
}
/// Info logger convenience method for SideStore logging
/// - Parameters:
/// - message: String or format string
/// - args: optional args for format string
@inlinable
public func ILOG(_ message: StaticString, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, _ args: CVarArg...) {
OSLog.info(message, args)
}
/// Debug logger convenience method for SideStore logging
/// - Parameters:
/// - message: String or format string
/// - args: optional args for format string
@inlinable
public func DLOG(_ message: StaticString, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, _ args: CVarArg...) {
OSLog.debug(message, args)
}
// mark: Helpers

View File

@@ -0,0 +1,19 @@
//
// Source+Trusted.swift
// SideStore
//
// Created by Fabian Thies on 04.02.23.
// Copyright © 2023 SideStore. All rights reserved.
//
import AltStoreCore
extension Source {
var isOfficial: Bool {
self.identifier == Source.altStoreIdentifier
}
var isTrusted: Bool {
UserDefaults.shared.trustedSourceIDs?.contains(self.identifier) ?? false
}
}

View File

@@ -0,0 +1,17 @@
//
// StoreApp+Searchable.swift
// SideStore
//
// Created by Fabian Thies on 01.12.22.
// Copyright © 2022 SideStore. All rights reserved.
//
import AltStoreCore
extension StoreApp: Filterable {
func matches(_ searchText: String) -> Bool {
searchText.isEmpty ||
self.name.lowercased().contains(searchText.lowercased()) ||
self.developerName.lowercased().contains(searchText.lowercased())
}
}

View File

@@ -0,0 +1,15 @@
//
// StoreApp+SideStore.swift
// SideStore
//
// Created by naturecodevoid on 4/9/23.
// Copyright © 2023 SideStore. All rights reserved.
//
import AltStoreCore
extension StoreApp {
var isSideStore: Bool {
self.bundleIdentifier == Bundle.Info.appbundleIdentifier
}
}

View File

@@ -0,0 +1,19 @@
//
// StoreApp+Trusted.swift
// SideStore
//
// Created by Fabian Thies on 04.02.23.
// Copyright © 2023 SideStore. All rights reserved.
//
import AltStoreCore
extension StoreApp {
var isFromOfficialSource: Bool {
self.source?.isOfficial ?? false
}
var isFromTrustedSource: Bool {
self.source?.isTrusted ?? false
}
}

View File

@@ -0,0 +1,45 @@
//
// UIApplication+SideStore.swift
// SideStore
//
// Created by naturecodevoid on 5/20/23.
// Copyright © 2023 SideStore. All rights reserved.
//
extension UIApplication {
static var keyWindow: UIWindow? {
UIApplication.shared.windows.filter { $0.isKeyWindow }.first
}
static var topController: UIViewController? {
guard var topController = keyWindow?.rootViewController else { return nil }
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
static func alert(
title: String? = nil,
message: String? = nil,
leftButton: (text: String, action: ((UIAlertAction) -> Void)?)? = nil,
rightButton: (text: String, action: ((UIAlertAction) -> Void)?)? = nil,
leftButtonStyle: UIAlertAction.Style = .default,
rightButtonStyle: UIAlertAction.Style = .default
) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
if let leftButton = leftButton {
alert.addAction(UIAlertAction(title: leftButton.text, style: leftButtonStyle, handler: leftButton.action))
}
if let rightButton = rightButton {
alert.addAction(UIAlertAction(title: rightButton.text, style: rightButtonStyle, handler: rightButton.action))
}
if rightButton == nil && leftButton == nil {
alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: ""), style: .default))
}
DispatchQueue.main.async {
topController?.present(alert, animated: true)
}
}
}

View File

@@ -0,0 +1,22 @@
//
// View+Hidden.swift
// SideStore
//
// Created by naturecodevoid on 2/18/23.
// Copyright © 2023 SideStore. All rights reserved.
//
import SwiftUI
// https://stackoverflow.com/a/59228385 (modified)
extension View {
@ViewBuilder func isHidden(_ hidden: Binding<Bool>, remove: Bool = false) -> some View {
if hidden.wrappedValue {
if !remove {
self.hidden()
}
} else {
self
}
}
}

View File

@@ -4,12 +4,23 @@
<dict>
<key>ALTAppGroups</key>
<array>
<string>group.com.rileytestut.AltStore</string>
<string>group.$(APP_GROUP_IDENTIFIER)</string>
<string>group.com.SideStore.SideStore</string>
</array>
<key>ALTDeviceID</key>
<string>00008110-000A68390A82801E</string>
<string>00008101-000129D63698001E</string>
<key>ALTServerID</key>
<string>1F7D5B55-79CE-4546-A029-D4DDC4AF3B6D</string>
<key>ALTPairingFile</key>
<string>&lt;insert pairing file here&gt;</string>
<key>ALTAnisetteURL</key>
<!--
for some reason, when we use the Info.plist preprocessor, 2 slashes in a row
removes the rest of the line and makes the plist invalid. to get around this,
we add a variable expansion ( $() ) in between the slashes that will ultimately
evaluate to nothing, keeping the original URL while keeping the plist valid.
-->
<string>http:/$()/ani.sidestore.io:6969</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDocumentTypes</key>
@@ -39,6 +50,8 @@
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
@@ -51,6 +64,7 @@
<key>CFBundleURLSchemes</key>
<array>
<string>altstore</string>
<string>sidestore</string>
</array>
</dict>
<dict>
@@ -61,6 +75,7 @@
<key>CFBundleURLSchemes</key>
<array>
<string>altstore-com.rileytestut.AltStore</string>
<string>sidestore-com.SideStore.SideStore</string>
</array>
</dict>
</array>
@@ -89,7 +104,7 @@
<string>_altserver._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>AltStore uses the local network to find and communicate with AltServer.</string>
<string>SideStore uses the local network to find and communicate with your device.</string>
<key>NSUserActivityTypes</key>
<array>
<string>RefreshAllIntent</string>
@@ -124,6 +139,11 @@
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
@@ -170,6 +190,37 @@
<string>ipa</string>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>com.apple.plist</string>
</array>
<key>UTTypeDescription</key>
<string>Mobile Device Pairing</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>org.sidestore.mobiledevicepairing</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mobiledevicepairing</string>
</array>
</dict>
</dict>
</array>
<key>UISupportsDocumentBrowser</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
<!--
#if MDC
-->
<key>NSAppleMusicUsageDescription</key>
<string>Full access to files on your device is required to apply the installd patch to remove the 3 app limit that free developer accounts have.</string>
<!--
#endif
-->
</dict>
</plist>

View File

@@ -11,7 +11,7 @@ import Foundation
import AltStoreCore
@available(iOS 14, *)
class IntentHandler: NSObject, RefreshAllIntentHandling
final class IntentHandler: NSObject, RefreshAllIntentHandling
{
private let queue = DispatchQueue(label: "io.altstore.IntentHandler")

View File

@@ -7,11 +7,17 @@
//
import UIKit
import SwiftUI
import Roxas
import EmotionalDamage
import minimuxer
import AltStoreCore
import UniformTypeIdentifiers
class LaunchViewController: RSTLaunchViewController
let pairingFileName = "ALTPairingFile.mobiledevicepairing"
final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDelegate
{
private var didFinishLaunching = false
@@ -35,10 +41,125 @@ class LaunchViewController: RSTLaunchViewController
override func viewDidLoad()
{
defer {
if UnstableFeatures.enabled(.swiftUI) {
let rootView = RootView()
.environment(\.managedObjectContext, DatabaseManager.shared.viewContext)
self.destinationViewController = UIHostingController(rootView: rootView)
} else {
// Create destinationViewController now so view controllers can register for receiving Notifications.
self.destinationViewController = self.storyboard!.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController
}
}
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
#if MDC
MDC.alertIfNotPatched()
#endif
// Create destinationViewController now so view controllers can register for receiving Notifications.
self.destinationViewController = self.storyboard!.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController
#if !targetEnvironment(simulator)
if UnstableFeatures.enabled(.onboarding) && !UserDefaults.standard.onboardingComplete {
self.showOnboarding()
return
}
start_em_proxy(bind_addr: Consts.Proxy.serverURL)
guard let pf = fetchPairingFile() else {
self.showOnboarding(enabledSteps: [.pairing])
return
}
start_minimuxer_threads(pf)
#endif
}
func showOnboarding(enabledSteps: [OnboardingStep] = OnboardingStep.allCases) {
let onboardingView = OnboardingView(onDismiss: { self.dismiss(animated: true) }, enabledSteps: enabledSteps)
.environment(\.managedObjectContext, DatabaseManager.shared.viewContext)
let navigationController = UINavigationController(rootViewController: UIHostingController(rootView: onboardingView))
navigationController.isNavigationBarHidden = true
navigationController.isModalInPresentation = true
self.present(navigationController, animated: true)
}
func fetchPairingFile() -> String? {
let filename = "ALTPairingFile.mobiledevicepairing"
let fm = FileManager.default
let documentsPath = fm.documentsDirectory.appendingPathComponent("/\(filename)")
if fm.fileExists(atPath: documentsPath.path), let contents = try? String(contentsOf: documentsPath), !contents.isEmpty {
print("Loaded ALTPairingFile from \(documentsPath.path)")
return contents
} else if
let appResourcePath = Bundle.main.url(forResource: "ALTPairingFile", withExtension: "mobiledevicepairing"),
fm.fileExists(atPath: appResourcePath.path),
let data = fm.contents(atPath: appResourcePath.path),
let contents = String(data: data, encoding: .utf8),
!contents.isEmpty {
print("Loaded ALTPairingFile from \(appResourcePath.path)")
return contents
} else if let plistString = Bundle.main.object(forInfoDictionaryKey: "ALTPairingFile") as? String, !plistString.isEmpty, !plistString.contains("insert pairing file here"){
print("Loaded ALTPairingFile from Info.plist")
return plistString
}
return nil
}
func displayError(_ msg: String) {
print(msg)
// Create a new alert
let dialogMessage = UIAlertController(title: "Error launching SideStore", message: msg, preferredStyle: .alert)
// Present alert to user
self.present(dialogMessage, animated: true, completion: nil)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
let url = urls[0]
let isSecuredURL = url.startAccessingSecurityScopedResource() == true
do {
// Read to a string
let data1 = try Data(contentsOf: urls[0])
let pairing_string = String(bytes: data1, encoding: .utf8)
if pairing_string == nil {
displayError("Unable to read pairing file")
}
// Save to a file for next launch
let pairingFile = FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)")
try pairing_string?.write(to: pairingFile, atomically: true, encoding: String.Encoding.utf8)
// Start minimuxer now that we have a file
start_minimuxer_threads(pairing_string!)
} catch {
displayError("Unable to read pairing file")
}
if (isSecuredURL) {
url.stopAccessingSecurityScopedResource()
}
controller.dismiss(animated: true, completion: nil)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
displayError("Choosing a pairing file was cancelled. Please re-open the app and try again.")
}
func start_minimuxer_threads(_ pairing_file: String) {
target_minimuxer_address()
let documentsDirectory = FileManager.default.documentsDirectory.absoluteString
do {
try start(pairing_file, documentsDirectory)
} catch {
try! FileManager.default.removeItem(at: FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)"))
displayError("minimuxer failed to start, please restart SideStore. \(error.message())")
}
set_debug(UserDefaults.shared.isDebugLoggingEnabled)
start_auto_mounter(documentsDirectory)
}
}
@@ -52,9 +173,21 @@ extension LaunchViewController
}
catch let error as NSError
{
let title = error.userInfo[NSLocalizedFailureErrorKey] as? String ?? NSLocalizedString("Unable to Launch AltStore", comment: "")
let title = error.userInfo[NSLocalizedFailureErrorKey] as? String ?? NSLocalizedString("Unable to Launch SideStore", comment: "")
let alertController = UIAlertController(title: title, message: error.localizedDescription, preferredStyle: .alert)
let errorDescription: String
if #available(iOS 14.5, *)
{
let errorMessages = [error.debugDescription] + error.underlyingErrors.map { ($0 as NSError).debugDescription }
errorDescription = errorMessages.joined(separator: "\n\n")
}
else
{
errorDescription = error.debugDescription
}
let alertController = UIAlertController(title: title, message: errorDescription, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Retry", comment: ""), style: .default, handler: { (action) in
self.handleLaunchConditions()
}))

View File

@@ -0,0 +1,177 @@
// Extension of MDC+AltStoreCore for the functionality AltStore uses
// The only reason we can't have it all in AltStore is because AltStoreCore requires one variable of MDC to determine the free app limit
import Foundation
import AltStoreCore
extension MDC {
#if MDC
enum PatchError: LocalizedError {
case NoFDA(error: String)
case FailedPatchd
var failureReason: String? {
switch (self) {
case .NoFDA(let error): return L10n.Remove3AppLimitView.Errors.noFDA(error)
case .FailedPatchd: return L10n.Remove3AppLimitView.Errors.failedPatchd
}
}
}
static func patch3AppLimit() async throws {
#if !targetEnvironment(simulator)
let res: PatchError? = await withCheckedContinuation { continuation in
grant_full_disk_access { error in
if let error = error {
continuation.resume(returning: PatchError.NoFDA(error: error.message()))
} else if !patch_installd() {
continuation.resume(returning: PatchError.FailedPatchd)
} else {
continuation.resume(returning: nil)
}
}
}
if let error = res {
throw error
}
#else
print("The patch would be running right now if you weren't using a simulator. It will stop \"running\" in 3 seconds.")
try await Task.sleep(nanoseconds: UInt64(3 * Double(NSEC_PER_SEC)))
// throw MDC.PatchError.NoFDA(error: "This is a test error")
#endif
UserDefaults.shared.lastInstalldPatchBootTime = bootTime()
UserDefaults.shared.hasPatchedInstalldEver = true
}
static func alertIfNotPatched() {
guard UserDefaults.shared.hasPatchedInstalldEver && !installdHasBeenPatched && isSupported else { return }
UIApplication.alert(
title: L10n.Remove3AppLimitView.title,
message: L10n.Remove3AppLimitView.NotAppliedAlert.message,
leftButton: (text: L10n.Remove3AppLimitView.NotAppliedAlert.apply, action: { _ in
Task {
do {
try await MDC.patch3AppLimit()
await UIApplication.alert(
title: L10n.Remove3AppLimitView.success
)
} catch {
await UIApplication.alert(
title: L10n.AsyncFallibleButton.error,
message: error.message()
)
}
}
}),
rightButton: (text: L10n.Remove3AppLimitView.NotAppliedAlert.continueWithout, action: nil)
)
}
#endif
private static let ios15 = OperatingSystemVersion(majorVersion: 15, minorVersion: 0, patchVersion: 0) // supported
private static let ios15_7_2 = OperatingSystemVersion(majorVersion: 15, minorVersion: 7, patchVersion: 2) // fixed
private static let ios16 = OperatingSystemVersion(majorVersion: 16, minorVersion: 0, patchVersion: 0) // supported
private static let ios16_2 = OperatingSystemVersion(majorVersion: 16, minorVersion: 2, patchVersion: 0) // fixed
static var isSupported: Bool {
#if targetEnvironment(simulator)
true
#else
(ProcessInfo.processInfo.isOperatingSystemAtLeast(ios15) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios15_7_2)) ||
(ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2))
#endif
}
}
#if MDC
// enum WhitelistPatchResult {
// case success, failure
// }
//
// let blankplist = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdC8+CjwvcGxpc3Q+Cg=="
//
// func patchWhiteList() {
// overwriteFileData(originPath: "/private/var/db/MobileIdentityData/AuthListBannedUpps.plist", replacementData: try! Data(base64Encoded: blankplist)!)
// overwriteFileData(originPath: "/private/var/db/MobileIdentityData/AuthListBannedCdHashes.plist", replacementData: try! Data(base64Encoded: blankplist)!)
// overwriteFileData(originPath: "/private/var/db/MobileIdentityData/Rejections.plist", replacementData: try! Data(base64Encoded: blankplist)!)
// }
//
// func overwriteFileData(originPath: String, replacementData: Data) -> Bool {
// #if false
// let documentDirectory = FileManager.default.urls(
// for: .documentDirectory,
// in: .userDomainMask
// )[0].path
//
// let pathToRealTarget = originPath
// let originPath = documentDirectory + originPath
// let origData = try! Data(contentsOf: URL(fileURLWithPath: pathToRealTarget))
// try! origData.write(to: URL(fileURLWithPath: originPath))
// #endif
//
// // open and map original font
// let fd = open(originPath, O_RDONLY | O_CLOEXEC)
// if fd == -1 {
// print("Could not open target file")
// return false
// }
// defer { close(fd) }
// // check size of font
// let originalFileSize = lseek(fd, 0, SEEK_END)
// guard originalFileSize >= replacementData.count else {
// print("Original file: \(originalFileSize)")
// print("Replacement file: \(replacementData.count)")
// print("File too big!")
// return false
// }
// lseek(fd, 0, SEEK_SET)
//
// // Map the font we want to overwrite so we can mlock it
// let fileMap = mmap(nil, replacementData.count, PROT_READ, MAP_SHARED, fd, 0)
// if fileMap == MAP_FAILED {
// print("Failed to map")
// return false
// }
// // mlock so the file gets cached in memory
// guard mlock(fileMap, replacementData.count) == 0 else {
// print("Failed to mlock")
// return true
// }
//
// // for every 16k chunk, rewrite
// print(Date())
// for chunkOff in stride(from: 0, to: replacementData.count, by: 0x4000) {
// print(String(format: "%lx", chunkOff))
// let dataChunk = replacementData[chunkOff..<min(replacementData.count, chunkOff + 0x4000)]
// var overwroteOne = false
// for _ in 0..<2 {
// let overwriteSucceeded = dataChunk.withUnsafeBytes { dataChunkBytes in
// unalign_csr(
// fd, Int64(chunkOff), dataChunkBytes.baseAddress, dataChunkBytes.count
// )
// }
// if overwriteSucceeded {
// overwroteOne = true
// print("Successfully overwrote!")
// break
// }
// print("try again?!")
// }
// guard overwroteOne else {
// print("Failed to overwrite")
// return false
// }
// }
// print(Date())
// print("Successfully overwrote!")
// return true
// }
//
// func readFile(path: String) -> String? {
// return (try? String?(String(contentsOfFile: path)) ?? "ERROR: Could not read from file! Are you running in the simulator or not unsandboxed?")
// }
#endif

View File

@@ -0,0 +1,33 @@
//
// MDC+AltStoreCore.swift
// AltStoreCore
//
// Created by naturecodevoid on 5/31/23.
// Copyright © 2023 SideStore. All rights reserved.
//
import Foundation
// Parts of MDC we need in AltStoreCore
// TODO: destroy AltStoreCore
public class MDC {
#if MDC
public static var installdHasBeenPatched: Bool {
guard let lastInstalldPatchBootTime = UserDefaults.shared.lastInstalldPatchBootTime else { return false }
return lastInstalldPatchBootTime == bootTime()
}
#endif
}
#if MDC
public func bootTime() -> Date? {
var tv = timeval()
var tvSize = MemoryLayout<timeval>.size
let err = sysctlbyname("kern.boottime", &tv, &tvSize, nil, 0)
guard err == 0, tvSize == MemoryLayout<timeval>.size else {
return nil
}
return Date(timeIntervalSince1970: Double(tv.tv_sec) + Double(tv.tv_usec) / 1_000_000.0)
}
#endif

View File

@@ -0,0 +1,99 @@
//
// Remove3AppLimitView.swift
// SideStore
//
// Created by naturecodevoid on 5/29/23.
// Copyright © 2023 SideStore. All rights reserved.
//
#if MDC
import SwiftUI
import AltStoreCore
fileprivate extension View {
func common() -> some View {
self
.padding()
.transition(.opacity.animation(.linear))
}
}
struct Remove3AppLimitView: View {
@ObservedObject private var iO = Inject.observer
@State var runningPatch = false
@State private var showErrorAlert = false
@State private var errorAlertMessage = ""
@State private var showSuccessAlert = false
@ViewBuilder
private var notSupported: some View {
Text(L10n.Remove3AppLimitView.notSupported)
}
@ViewBuilder
private var installdHasBeenPatched: some View {
Text(L10n.Remove3AppLimitView.alreadyPatched)
Text(L10n.Remove3AppLimitView.tenAppsInfo)
}
@ViewBuilder
private var applyPatch: some View {
Text(L10n.Remove3AppLimitView.patchInfo)
Text(L10n.Remove3AppLimitView.tenAppsInfo)
}
var body: some View {
VStack {
if !MDC.isSupported {
notSupported.common()
} else {
if MDC.installdHasBeenPatched {
installdHasBeenPatched.common()
} else {
applyPatch.common()
SwiftUI.Button(action: {
Task {
do {
guard !runningPatch else { return }
runningPatch = true
try await MDC.patch3AppLimit()
showSuccessAlert = true
} catch {
errorAlertMessage = error.message()
showErrorAlert = true
}
runningPatch = false
}
}) { Text(L10n.Remove3AppLimitView.applyPatch) }
.buttonStyle(FilledButtonStyle(isLoading: runningPatch, hideLabelOnLoading: false))
.padding()
}
}
Spacer()
}
.alert(isPresented: $showErrorAlert) {
Alert(
title: Text(L10n.AsyncFallibleButton.error),
message: Text(errorAlertMessage)
)
}
.alert(isPresented: $showSuccessAlert) {
Alert(
title: Text(L10n.Action.success),
message: Text(L10n.Remove3AppLimitView.success)
)
}
.navigationTitle(L10n.Remove3AppLimitView.title)
.enableInjection()
}
}
struct Remove3AppLimitView_Previews: PreviewProvider {
static var previews: some View {
Remove3AppLimitView()
}
}
#endif

View File

@@ -0,0 +1,8 @@
#ifdef MDC
#pragma once
@import Foundation;
/// Uses CVE-2022-46689 to grant the current app read/write access outside the sandbox.
void grant_full_disk_access(void (^_Nonnull completion)(NSError* _Nullable));
bool patch_installd(void);
#endif /* MDC */

Some files were not shown because too many files have changed in this diff Show More