Compare commits

...

27 Commits

Author SHA1 Message Date
Joseph Mattello
5b977092f5 refs #103 use redirected url as source url
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-02-15 16:23:44 -08:00
Joseph Mattello
c6915d1f91 refs #103 redirects are working
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-02-15 16:23:44 -08:00
Joseph Mattello
d6ea725329 Add Network service and default sessions
Signed-off-by: Joseph Mattello <mail@joemattiello.com>
2023-02-15 16:23:44 -08:00
naturecodevoid
79d677cf3c Revamp issue and PR templates (#253)
* Create config.yml

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

* Delete bug_report.md

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

* Create bug_report.yml

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

* Create feature_request.yml

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

* Create pull_request_template.md

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

---------

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

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

* My Apps: Fix incorrect app name on first launch

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

* Trusted Sources: Add stable and beta
2023-02-02 08:05:27 -08:00
naturecodevoid
4d8438a6b6 Update minimuxer 2023-02-01 20:05:26 -08:00
naturecodevoid
f611244e35 Add PR suffix to version in PR workflow
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-26 17:13:32 -08:00
naturecodevoid
546a978d3b Update .gitignore 2023-01-25 06:50:06 -08:00
naturecodevoid
70b23fb073 Merge remote-tracking branch 'upstream/develop' into develop 2023-01-19 17:39:11 -08:00
naturecodevoid
a56ca597d6 Fix build errors 2023-01-19 17:37:43 -08:00
naturecodevoid
679e0228a8 Remove debug
Signed-off-by: naturecodevoid <44983869+naturecodevoid@users.noreply.github.com>
2023-01-19 08:30:47 -08:00
naturecodevoid
e153394323 Merge branch 'develop' of https://github.com/naturecodevoid/SideStore into develop 2023-01-19 07:56:52 -08:00
naturecodevoid
5bd1fcfcfd Merge branch 'develop' of https://github.com/SideStore/SideStore into develop 2023-01-19 07:54:25 -08:00
naturecodevoid
2a392ddc44 SemVer version comparison 2023-01-19 07:52:47 -08:00
Joelle Stickney
b5cb8bc0d9 Updated Patreon Link 2023-01-18 14:41:24 -05:00
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
21 changed files with 423 additions and 138 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

@@ -74,6 +74,9 @@ jobs:
# - name: Build minimuxer
# run: cd Dependencies/minimuxer && cargo build --release --target aarch64-apple-ios
- name: Add PR suffix to version
run: sed -e '/MARKETING_VERSION = .*/s/$/-pr.${{ github.event.pull_request.number }}/' -i '' Build.xcconfig
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1
with:

2
.gitmodules vendored
View File

@@ -21,7 +21,7 @@
url = https://github.com/libimobiledevice/libimobiledevice-glue
[submodule "Dependencies/minimuxer"]
path = Dependencies/minimuxer
url = https://github.com/jkcoxson/minimuxer
url = https://github.com/SideStore/minimuxer
[submodule "Dependencies/libfragmentzip"]
path = Dependencies/libfragmentzip
url = https://github.com/SideStore/libfragmentzip.git

View File

@@ -21,6 +21,7 @@
19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */; };
4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; };
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; };
99C4EF4D2979132100CB538D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = 99C4EF4C2979132100CB538D /* SemanticVersion */; };
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, ); }; };
B33FFBA8295F8E98002259E6 /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B343F894295F7F9B002B1159 /* libfragmentzip.a */; };
@@ -42,6 +43,7 @@
B39575F5284F29E20080B4FF /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B39575F4284F29E20080B4FF /* Roxas.framework */; };
B39F16132918D7C5002E9404 /* Consts.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39F16122918D7C5002E9404 /* Consts.swift */; };
B39F16152918D7DA002E9404 /* Consts+Proxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39F16142918D7DA002E9404 /* Consts+Proxy.swift */; };
B3C053B7295F921E0079DB81 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3C053B6295F921E0079DB81 /* NetworkService.swift */; };
B3C395F1284F2DE700DA9E2F /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F0284F2DE700DA9E2F /* KeychainAccess */; };
B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F3284F35DD00DA9E2F /* Nuke */; };
B3C395F7284F362400DA9E2F /* AppCenterAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F6284F362400DA9E2F /* AppCenterAnalytics */; };
@@ -532,6 +534,7 @@
B39575F4284F29E20080B4FF /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B39F16122918D7C5002E9404 /* Consts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consts.swift; sourceTree = "<group>"; };
B39F16142918D7DA002E9404 /* Consts+Proxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Consts+Proxy.swift"; sourceTree = "<group>"; };
B3C053B6295F921E0079DB81 /* NetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
B3C39606284F4C8400DA9E2F /* CodeSigning.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CodeSigning.xcconfig; sourceTree = "<group>"; };
B3C39607284F4C8400DA9E2F /* Build.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Build.xcconfig; sourceTree = "<group>"; };
B3C39608284F4C8400DA9E2F /* CodeSigning.xcconfig.sample */ = {isa = PBXFileReference; lastKnownFileType = text; path = CodeSigning.xcconfig.sample; sourceTree = "<group>"; };
@@ -896,6 +899,7 @@
buildActionMask = 2147483647;
files = (
B3C395F1284F2DE700DA9E2F /* KeychainAccess in Frameworks */,
99C4EF4D2979132100CB538D /* SemanticVersion in Frameworks */,
4879A95F2861046500FC1BBD /* AltSign in Frameworks */,
B39575F5284F29E20080B4FF /* Roxas.framework in Frameworks */,
);
@@ -1024,6 +1028,14 @@
path = Consts;
sourceTree = "<group>";
};
B3C053B5295F921E0079DB81 /* Services */ = {
isa = PBXGroup;
children = (
B3C053B6295F921E0079DB81 /* NetworkService.swift */,
);
path = Services;
sourceTree = "<group>";
};
BF0DCA642433BDE200E3A595 /* Analytics */ = {
isa = PBXGroup;
children = (
@@ -1558,6 +1570,7 @@
BF3D648922E79A7700E9056B /* Types */,
BFD2479D2284FBBD00981D42 /* Extensions */,
BFD247962284D7C100981D42 /* Resources */,
B3C053B5295F921E0079DB81 /* Services */,
BF6C8FA8242935CA00125131 /* Dependencies */,
BFD247972284D7D800981D42 /* Supporting Files */,
1920B04E2924AC8300744F60 /* Settings.bundle */,
@@ -1937,11 +1950,13 @@
buildRules = (
);
dependencies = (
99C4EF51297994E200CB538D /* PBXTargetDependency */,
);
name = AltStoreCore;
packageProductDependencies = (
B3C395F0284F2DE700DA9E2F /* KeychainAccess */,
4879A95E2861046500FC1BBD /* AltSign */,
99C4EF4C2979132100CB538D /* SemanticVersion */,
);
productName = AltStoreCore;
productReference = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */;
@@ -2060,6 +2075,7 @@
B3C395FD284F3C0900DA9E2F /* XCRemoteSwiftPackageReference "STPrivilegedTask" */,
4879A95D2861046500FC1BBD /* XCRemoteSwiftPackageReference "AltSign" */,
4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */,
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
);
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
projectDirPath = "";
@@ -2468,6 +2484,7 @@
BF88F97224F8727D00BB75DF /* AppManagerErrors.swift in Sources */,
B39F16152918D7DA002E9404 /* Consts+Proxy.swift in Sources */,
BF6C8FAE2429597900125131 /* BannerCollectionViewCell.swift in Sources */,
B3C053B7295F921E0079DB81 /* NetworkService.swift in Sources */,
BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */,
BFE60742231B07E6002B0E8E /* SettingsHeaderFooterView.swift in Sources */,
BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */,
@@ -2543,6 +2560,10 @@
isa = PBXTargetDependency;
productRef = 191E5FD9290AFA49001A3B7C /* OpenSSL */;
};
99C4EF51297994E200CB538D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 99C4EF50297994E200CB538D /* SemanticVersion */;
};
B343F86F295F76FD002B1159 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "minimuxer-staticlib";
@@ -3346,6 +3367,14 @@
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" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
@@ -3417,6 +3446,16 @@
package = 4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "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 */ = {
isa = XCSwiftPackageProductDependency;
package = B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */;

View File

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

View File

@@ -754,7 +754,7 @@ private extension MyAppsViewController
{
let downloadProgress = Progress.discreteProgress(totalUnitCount: 100)
downloadOperation = RSTAsyncBlockOperation { (operation) in
let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in
let downloadTask = AppServices.network.session.downloadTask(with: url) { (fileURL, response, error) in
do
{
let (fileURL, _) = try Result((fileURL, response), error).get()

View File

@@ -39,8 +39,8 @@ final class DownloadAppOperation: ResultOperation<ALTApplication>
private var sourceURL: URL?
private let destinationURL: URL
private let session = URLSession(configuration: .default)
private let temporaryDirectory = FileManager.default.uniqueTemporaryURL()
private let session: URLSession = AppServices.network.backgroundSession
private let temporaryDirectory: URL = FileManager.default.uniqueTemporaryURL()
init(app: AppProtocol, destinationURL: URL, context: AppOperationContext)
{

View File

@@ -34,30 +34,31 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>
let url = AnisetteManager.currentURL
DLOG("Anisette URL: %@", url.absoluteString)
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
do {
// make sure this JSON is in the format we expect
// convert data to json
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
// try to read out a dictionary
//for some reason serial number isn't needed but it doesn't work unless it has a value
let formattedJSON: [String: String] = ["machineID": json["X-Apple-I-MD-M"]!, "oneTimePassword": json["X-Apple-I-MD"]!, "localUserID": json["X-Apple-I-MD-LU"]!, "routingInfo": json["X-Apple-I-MD-RINFO"]!, "deviceUniqueIdentifier": json["X-Mme-Device-Id"]!, "deviceDescription": json["X-MMe-Client-Info"]!, "date": json["X-Apple-I-Client-Time"]!, "locale": json["X-Apple-Locale"]!, "timeZone": json["X-Apple-I-TimeZone"]!, "deviceSerialNumber": "1"]
if let anisette = ALTAnisetteData(json: formattedJSON) {
let task = AppServices.network.session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
do {
// make sure this JSON is in the format we expect
// convert data to json
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
// try to read out a dictionary
//for some reason serial number isn't needed but it doesn't work unless it has a value
let formattedJSON: [String: String] = ["machineID": json["X-Apple-I-MD-M"]!, "oneTimePassword": json["X-Apple-I-MD"]!, "localUserID": json["X-Apple-I-MD-LU"]!, "routingInfo": json["X-Apple-I-MD-RINFO"]!, "deviceUniqueIdentifier": json["X-Mme-Device-Id"]!, "deviceDescription": json["X-MMe-Client-Info"]!, "date": json["X-Apple-I-Client-Time"]!, "locale": json["X-Apple-Locale"]!, "timeZone": json["X-Apple-I-TimeZone"]!, "deviceSerialNumber": "1"]
if let anisette = ALTAnisetteData(json: formattedJSON) {
DLOG("Anisette used: %@", formattedJSON)
self.finish(.success(anisette))
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
self.finish(.failure(error))
}
}
task.resume()
self.finish(.success(anisette))
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
self.finish(.failure(error))
}
}
task.resume()
}
}

View File

@@ -12,13 +12,51 @@ import CoreData
import AltStoreCore
import Roxas
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
String(text[Range($0.range, in: text)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
func containsRedirect(_ response: URLResponse?, data: Data?) -> String? {
if let httpResponse = response as? HTTPURLResponse {
print("Request status code: \(httpResponse.statusCode)")
print("Request headers: \(httpResponse.allHeaderFields.debugDescription)")
guard let data = data else {
print("Request error: missing data")
return nil
}
let rawHttp = String(decoding: data, as: UTF8.self)
let regex = "url=((https|http):\\/\\/[\\S]*)\">"
guard var redirectURL = matches(for: regex, in: rawHttp).first else {
return nil
}
redirectURL = redirectURL.replacingOccurrences(of: "url=", with: "")
redirectURL = redirectURL.replacingOccurrences(of: "\">", with: "")
print("redirectURL: \(redirectURL)")
return redirectURL
} else {
return nil
}
}
@objc(FetchSourceOperation)
final class FetchSourceOperation: ResultOperation<Source>
{
let sourceURL: URL
let managedObjectContext: NSManagedObjectContext
private let session: URLSession
private let session: URLSession = AppServices.network.sessionNoCache
private lazy var dateFormatter: ISO8601DateFormatter = {
let dateFormatter = ISO8601DateFormatter()
@@ -29,79 +67,98 @@ final class FetchSourceOperation: ResultOperation<Source>
{
self.sourceURL = sourceURL
self.managedObjectContext = managedObjectContext
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
configuration.urlCache = nil
self.session = URLSession(configuration: configuration)
}
override func main()
{
super.main()
let dataTask = self.session.dataTask(with: self.sourceURL) { (data, response, error) in
let childContext = DatabaseManager.shared.persistentContainer.newBackgroundContext(withParent: self.managedObjectContext)
childContext.mergePolicy = NSOverwriteMergePolicy
childContext.perform {
do
{
let (data, _) = try Result((data, response), error).get()
let decoder = AltStoreCore.JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let text = try container.decode(String.self)
// Full ISO8601 Format.
self.dateFormatter.formatOptions = [.withFullDate, .withFullTime, .withTimeZone]
if let date = self.dateFormatter.date(from: text)
{
return date
}
// Just date portion of ISO8601.
self.dateFormatter.formatOptions = [.withFullDate]
if let date = self.dateFormatter.date(from: text)
{
return date
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Date is in invalid format.")
})
decoder.managedObjectContext = childContext
decoder.sourceURL = self.sourceURL
let source = try decoder.decode(Source.self, from: data)
let identifier = source.identifier
try childContext.save()
self.managedObjectContext.perform {
if let source = Source.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Source.identifier), identifier), in: self.managedObjectContext)
{
self.finish(.success(source))
}
else
{
self.finish(.failure(OperationError.noSources))
}
}
}
catch
{
self.managedObjectContext.perform {
self.finish(.failure(error))
}
}
}
}
loadSource(self.sourceURL)
}
private func loadSource(_ url: URL) {
let dataTask = createDataTask(with: url)
self.progress.addChild(dataTask.progress, withPendingUnitCount: 1)
dataTask.resume()
}
private func createDataTask(with url: URL) -> URLSessionDataTask {
let dataTask = self.session.dataTask(with: url) { (data, response, error) in
// Test code for http redirect HTML, though seems I got jekyll/sidestore to work without this now - @JoeMatt
if let error = error {
print("Request error: \(error)")
// TODO: Handle error
self.finish(.failure(error))
return
}
if let redirect = containsRedirect(response, data: data), let redirectURL = URL(string: redirect) {
DispatchQueue.main.async {
self.loadSource(redirectURL)
}
} else {
self.processJSON(data: data, response: response, error: error)
}
}
return dataTask
}
private func processJSON(data: Data?, response: URLResponse?, error: Error?) {
let childContext = DatabaseManager.shared.persistentContainer.newBackgroundContext(withParent: self.managedObjectContext)
childContext.mergePolicy = NSOverwriteMergePolicy
childContext.perform {
do
{
let (data, _) = try Result((data, response), error).get()
let decoder = AltStoreCore.JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let text = try container.decode(String.self)
// Full ISO8601 Format.
self.dateFormatter.formatOptions = [.withFullDate, .withFullTime, .withTimeZone]
if let date = self.dateFormatter.date(from: text)
{
return date
}
// Just date portion of ISO8601.
self.dateFormatter.formatOptions = [.withFullDate]
if let date = self.dateFormatter.date(from: text)
{
return date
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Date is in invalid format.")
})
decoder.managedObjectContext = childContext
// Note: This may need to be response.url instead, to handle redirects @JoeMatt
decoder.sourceURL = response?.url ?? self.sourceURL
let source = try decoder.decode(Source.self, from: data)
let identifier = source.identifier
try childContext.save()
self.managedObjectContext.perform {
if let source = Source.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Source.identifier), identifier), in: self.managedObjectContext)
{
self.finish(.success(source))
}
else
{
self.finish(.failure(OperationError.noSources))
}
}
}
catch
{
self.managedObjectContext.perform {
self.finish(.failure(error))
}
}
}
}
}

View File

@@ -37,8 +37,8 @@ final class FetchTrustedSourcesOperation: ResultOperation<[FetchTrustedSourcesOp
override func main()
{
super.main()
let dataTask = URLSession.shared.dataTask(with: .trustedSources) { (data, response, error) in
let session: URLSession = AppServices.network.session
let dataTask = session.dataTask(with: .trustedSources) { (data, response, error) in
do
{
if let response = response as? HTTPURLResponse

View File

@@ -43,7 +43,7 @@ final class UpdatePatronsOperation: ResultOperation<Void>
{
super.main()
let dataTask = URLSession.shared.dataTask(with: .patreonInfo) { (data, response, error) in
let dataTask = AppServices.network.session.dataTask(with: .patreonInfo) { (data, response, error) in
do
{
if let response = response as? HTTPURLResponse

View File

@@ -0,0 +1,94 @@
//
// NetworkService.swift
// SideStore
//
// Created by Joseph Mattiello on 11/6/22.
// Copyright © 2022 Riley Testut. All rights reserved.
//
import Foundation
public protocol Services {
var network: any NetworkService { get }
}
public protocol NetworkService {
var session: URLSession { get }
var sessionNoCache: URLSession { get }
var backgroundSession: URLSession { get }
}
public struct DefaultServices: Services {
public var network: NetworkService = AltNetworkService()
}
let AppServices = DefaultServices()
final public class AltNetworkDelegate: NSObject, URLSessionDelegate, URLSessionTaskDelegate {
public override init() {
super.init()
}
public struct Options: OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
public static let redirect = Options(rawValue: 1 << 0)
public static let all: Options = [.redirect]
}
var options: Options = .all
// public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
// if options.contains(.redirect) {
// completionHandler(request)
// } else {
// completionHandler(nil)
// }
// }
public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest) async -> URLRequest? {
if options.contains(.redirect) {
return request
} else {
return nil
}
}
}
public final class AltNetworkService: NetworkService {
let delegate = AltNetworkDelegate()
lazy var delegateQueue: OperationQueue = {
let queue = OperationQueue.init()
queue.name = "com.sidestore.NetworkService.serialOperationQueue"
queue.maxConcurrentOperationCount = 1
return queue
}()
public lazy var session: URLSession = {
let configuration: URLSessionConfiguration = URLSessionConfiguration.default
configuration.httpShouldSetCookies = true
configuration.httpShouldUsePipelining = true
let session = URLSession.init(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue)
return session
}()
public lazy var sessionNoCache: URLSession = {
let configuration: URLSessionConfiguration = URLSessionConfiguration.default
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
configuration.urlCache = nil
let session = URLSession.init(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue)
return session
}()
static let backgroundSessionIdentifier = "SideStoreBackgroundSession"
public lazy var backgroundSession: URLSession = {
let configuration: URLSessionConfiguration = URLSessionConfiguration.background(withIdentifier: AltNetworkService.backgroundSessionIdentifier)
let session = URLSession.init(configuration: configuration, delegate: delegate, delegateQueue: nil)
return session
}()
}

View File

@@ -16,9 +16,10 @@
<key>Key</key>
<string>customAnisetteURL</string>
<key>DefaultValue</key>
<string>http://191.101.206.188:6969</string>
<string>http://ani.sidestore.io</string>
<key>Titles</key>
<array>
<string>SideStore</string>
<string>Macley (US)</string>
<string>Macley (DE)</string>
<string>DrPudding</string>
@@ -31,6 +32,7 @@
</array>
<key>Values</key>
<array>
<string>http://ani.sidestore.io</string>
<string>http://us1.sternserv.tech</string>
<string>http://de1.sternserv.tech</string>
<string>https://sign.rheaa.xyz</string>

View File

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

View File

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

View File

@@ -10,6 +10,7 @@ import Foundation
import CoreData
import AltSign
import SemanticVersion
// Free developer accounts are limited to only 3 active sideloaded apps at a time as of iOS 13.3.1.
public let ALTActiveAppsLimit = 3
@@ -59,6 +60,21 @@ public class InstalledApp: NSManagedObject, InstalledAppProtocol
return self.storeApp == nil
}
@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
}
@@ -147,8 +163,8 @@ public extension InstalledApp
class func updatesFetchRequest() -> NSFetchRequest<InstalledApp>
{
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K != nil AND %K != %K",
#keyPath(InstalledApp.isActive), #keyPath(InstalledApp.storeApp), #keyPath(InstalledApp.version), #keyPath(InstalledApp.storeApp.latestVersion.version))
fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K == YES",
#keyPath(InstalledApp.isActive), #keyPath(InstalledApp.hasUpdate))
return fetchRequest
}