Compare commits

..

7 Commits

Author SHA1 Message Date
polymo1
430ed56ca9 ci: actually use beta in pr.yml
Signed-off-by: polymo1 <git@wesbryie.com>
2025-09-02 18:22:54 -04:00
polymo1
c74dc28773 ci: fix version number in pr.yml
Signed-off-by: polymo1 <git@wesbryie.com>
2025-09-02 18:18:12 -04:00
polymo1
5058eb7709 ci: update pr.yml to Xcode 26 Beta
Signed-off-by: polymo1 <git@wesbryie.com>
2025-09-02 18:12:57 -04:00
polymo1
242429b161 ci: change stable to Xcode 26 beta
Signed-off-by: polymo1 <git@wesbryie.com>
2025-09-02 18:09:11 -04:00
mahee96
724e8db980 - [ConsoleLogger]: Fix writing large data by chunking + stale handle capture 2025-08-06 02:03:18 +05:30
mahee96
669d33183e - [ConsoleLogger]: Fix race conditions during shutdown and possible crashes by writing to closed stream handler 2025-07-30 23:35:24 +05:30
CelloSerenity
a12b6cd62b Small fixes (#1020)
* here

* Update PatreonComponents.swift

Signed-off-by: CelloSerenity <195480169+CelloSerenity@users.noreply.github.com>

* Update ErrorLogViewController.swift

Signed-off-by: CelloSerenity <195480169+CelloSerenity@users.noreply.github.com>

* Update README.md

Signed-off-by: CelloSerenity <195480169+CelloSerenity@users.noreply.github.com>

* Update trustedapps.json

Signed-off-by: CelloSerenity <195480169+CelloSerenity@users.noreply.github.com>

* Revert "Update trustedapps.json"

This reverts commit 10a38895ea.

* Update Build.xcconfig

Signed-off-by: CelloSerenity <195480169+CelloSerenity@users.noreply.github.com>

---------

Signed-off-by: CelloSerenity <195480169+CelloSerenity@users.noreply.github.com>
2025-07-18 21:24:55 -04:00
9 changed files with 89 additions and 45 deletions

View File

@@ -12,8 +12,8 @@ jobs:
fail-fast: false
matrix:
include:
- os: 'macos-14'
version: '16.1'
- os: 'macos-15'
version: '26.0-beta'
runs-on: ${{ matrix.os }}
steps:

View File

@@ -13,7 +13,7 @@ jobs:
matrix:
include:
- os: 'macos-15'
version: '16.2'
version: '26.0'
runs-on: ${{ matrix.os }}
steps:

View File

@@ -170,7 +170,7 @@ class MyAppsViewController: UICollectionViewController, PeekPopPreviewing
var minimuxerStatus: Bool {
// added isMinimuxerStatusCheckEnabled to forcefully ignore minimuxer status if status check is disabled in settings
guard !UserDefaults.standard.isMinimuxerStatusCheckEnabled || minimuxer.ready() else {
ToastView(error: (OperationError.noWiFi as NSError).withLocalizedTitle("No WiFi or VPN!")).show(in: self)
ToastView(error: (OperationError.noWiFi as NSError).withLocalizedTitle("No Wi-Fi or VPN!")).show(in: self)
return false
}
return true

View File

@@ -220,16 +220,16 @@ struct OperationError: ALTLocalizedError {
case .openAppFailed:
let appName = self.appName ?? NSLocalizedString("The app", comment: "")
return String(format: NSLocalizedString("SideStore was denied permission to launch %@.", comment: ""), appName)
case .noWiFi: return NSLocalizedString("You do not appear to be connected to WiFi and/or StosVPN!\nSideStore will never be able to install or refresh applications without WiFi and the StosVPN.", comment: "")
case .tooNewError: return NSLocalizedString("iOS 17 has changed how JIT is enabled therefore SideStore cannot enable it without SideJITServer at this time, sorry for any inconvenience.\nWe will let everyone know once we have a solution!", comment: "")
case .unableToConnectSideJIT: return NSLocalizedString("Unable to connect to SideJITServer Please check that you are on the Same Wi-Fi and your Firewall has been set correctly", comment: "")
case .unableToRespondSideJITDevice: return NSLocalizedString("SideJITServer is unable to connect to your iDevice Please make sure you have paired your Device by doing 'SideJITServer -y' or try Refreshing SideJITServer from Settings", comment: "")
case .wrongSideJITIP: return NSLocalizedString("Incorrect SideJITServer IP Please make sure that you are on the Samw Wifi as SideJITServer", comment: "")
case .refreshsidejit: return NSLocalizedString("Unable to find App Please try Refreshing SideJITServer from Settings", comment: "")
case .anisetteV1Error: return NSLocalizedString("An error occurred when getting anisette data from a V1 server: %@. Try using another anisette server.", comment: "")
case .provisioningError: return NSLocalizedString("An error occurred when provisioning: %@ %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "")
case .anisetteV3Error: return NSLocalizedString("An error occurred when getting anisette data from a V3 server: %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "")
case .cacheClearError: return NSLocalizedString("An error occurred while clearing cache: %@", comment: "")
case .noWiFi: return NSLocalizedString("You do not appear to be connected to Wi-Fi and/or StosVPN!\nSideStore will never be able to install or refresh applications without Wi-Fi and StosVPN.", comment: "")
case .tooNewError: return NSLocalizedString("iOS 17 has changed how JIT is enabled, therefore, SideStore cannot enable JIT without SideJITServer, StikDebug, or SideStore-nightly at this time, sorry for any inconvenience.", comment: "")
case .unableToConnectSideJIT: return NSLocalizedString("Unable to connect to SideJITServer. Please check that you are on the same Wi-Fi of and your Firewall has been set correctly on your server", comment: "")
case .unableToRespondSideJITDevice: return NSLocalizedString("SideJITServer is unable to connect to your iDevice. Please make sure you have paired your Device by running 'SideJITServer -y', or try refreshing SideJITServer from Settings", comment: "")
case .wrongSideJITIP: return NSLocalizedString("Incorrect SideJITServer IP. Please make sure that you are on the same Wi-Fi as SideJITServer", comment: "")
case .refreshsidejit: return NSLocalizedString("Unable to find app; Please try refreshing SideJITServer from Settings", comment: "")
case .anisetteV1Error: return NSLocalizedString("An error occurred while getting anisette data from a V1 server: %@. Try using another anisette server.", comment: "")
case .provisioningError: return NSLocalizedString("An error occurred while provisioning: %@ %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "")
case .anisetteV3Error: return NSLocalizedString("An error occurred while getting anisette data from a V3 server: %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "")
case .cacheClearError: return NSLocalizedString("An error occurred while clearing the cache: %@", comment: "")
case .SideJITIssue: return NSLocalizedString("An error occurred while using SideJIT: %@", comment: "")
case .refreshAppFailed:
@@ -260,7 +260,7 @@ struct OperationError: ALTLocalizedError {
var recoverySuggestion: String? {
switch self.code
{
case .noWiFi: return NSLocalizedString("Make sure the VPN is toggled on and you are connected to any WiFi network!", comment: "")
case .noWiFi: return NSLocalizedString("Make sure StosVPN is toggled on and you are connected to any Wi-Fi network!", comment: "")
case .serverNotFound: return NSLocalizedString("Make sure you're on the same Wi-Fi network as a computer running AltServer, or try connecting this device to your computer via USB.", comment: "")
case .maximumAppIDLimitReached:
let baseMessage = NSLocalizedString("Delete sideloaded apps to free up App ID slots.", comment: "")
@@ -308,7 +308,7 @@ extension MinimuxerError: LocalizedError {
case .NoDevice:
return NSLocalizedString("Cannot fetch the device from the muxer", comment: "")
case .NoConnection:
return NSLocalizedString("Unable to connect to the device, make sure StosVPN is enabled and you're connected to WiFi. This could mean an invalid pairing.", comment: "")
return NSLocalizedString("Unable to connect to the device, make sure StosVPN is enabled and you're connected to Wi-Fi. This could mean an invalid pairing.", comment: "")
case .PairingFile:
return NSLocalizedString("Invalid pairing file. Your pairing file either didn't have a UDID, or it wasn't a valid plist. Please use idevice_pair to regenerate it", comment: "")

View File

@@ -380,13 +380,8 @@ private extension ErrorLogViewController
func searchFAQ(for loggedError: LoggedError)
{
let baseURL = URL(string: "https://faq.altstore.io/getting-started/error-codes")!
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)!
let query = [loggedError.domain, "\(loggedError.error.displayCode)"].joined(separator: "+")
components.queryItems = [URLQueryItem(name: "q", value: query)]
let safariViewController = SFSafariViewController(url: components.url ?? baseURL)
let staticURL = URL(string: "https://docs.sidestore.io/docs/troubleshooting/error-codes")!
let safariViewController = SFSafariViewController(url: staticURL)
safariViewController.preferredControlTintColor = .altPrimary
self.present(safariViewController, animated: true)
}

View File

@@ -82,7 +82,7 @@ final class AboutPatreonHeaderView: UICollectionReusableView
imageView.layer.cornerRadius = imageView.bounds.midY
}
for button in [self.supportButton, self.accountButton].compactMap({$0})
for button in [self.supportButton, self.accountButton, self.twitterButton, self.instagramButton].compactMap({$0})
{
button.clipsToBounds = true
button.layer.cornerRadius = 16

View File

@@ -1,8 +1,8 @@
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
MARKETING_VERSION = 0.6.2
CURRENT_PROJECT_VERSION = 0602
MARKETING_VERSION = 0.6.3
CURRENT_PROJECT_VERSION = 0603
// Vars to be overwritten by `CodeSigning.xcconfig` if exists
DEVELOPMENT_TEAM = S32Z3HMYVQ

View File

@@ -28,10 +28,10 @@ Why iOS 15? Targeting such a recent version of iOS allows us to accelerate devel
SideStore is a just regular, sandboxed iOS application. The AltStore app target contains the vast majority of SideStore's functionality, including all the logic for downloading and updating apps through SideStore. SideStore makes heavy use of standard iOS frameworks and technologies most iOS developers are familiar with.
### EM Proxy
[SideServer mobile](https://github.com/jkcoxson/em_proxy) powers the defining feature of SideStore: untethered app installation. By leveraging an App Store app with additional entitlements (WireGuard or StosVPN) to create the VPN tunnel for us, it allows SideStore to take advantage of [Jitterbug](https://github.com/osy/Jitterbug)'s loopback method without requiring a paid developer account.
[SideServer mobile](https://github.com/jkcoxson/em_proxy) powers the defining feature of SideStore: untethered app installation. By leveraging a custom-built App Store app with additional entitlements (StosVPN) to create the VPN tunnel for us, it allows SideStore to take advantage of [Jitterbug](https://github.com/osy/Jitterbug)'s loopback method without requiring a paid developer account.
### Minimuxer
[Minimuxer](https://github.com/jkcoxson/minimuxer) is a lockdown muxer that can run inside iOSs sandbox. It replicates Apples usbmuxd protocol on MacOS to “discover” devices to interface with wireguard On-Device.
[Minimuxer](https://github.com/jkcoxson/minimuxer) is a lockdown muxer that can run inside iOSs sandbox. It replicates Apples usbmuxd protocol on MacOS to “discover” devices to interface with StosVPN On-Device.
### Roxas
[Roxas](https://github.com/rileytestut/roxas) is Riley Testut's internal framework from AltStore used across many of their iOS projects, developed to simplify a variety of common tasks used in iOS development.

View File

@@ -59,48 +59,97 @@ public class AbstractConsoleLogger<T: OutputStream>: ConsoleLogger{
originalStdout = dup(STDOUT_FILENO)
originalStderr = dup(STDERR_FILENO)
let redirectedOutStream = self.outPipe?.fileHandleForWriting.fileDescriptor ?? -1
let redirectedErrStream = self.errPipe?.fileHandleForWriting.fileDescriptor ?? -1
// Redirect stdout and stderr to our pipes
dup2(self.outPipe?.fileHandleForWriting.fileDescriptor ?? -1, STDOUT_FILENO)
dup2(self.errPipe?.fileHandleForWriting.fileDescriptor ?? -1, STDERR_FILENO)
dup2(redirectedOutStream, STDOUT_FILENO)
dup2(redirectedErrStream, STDERR_FILENO)
// Disable libc-level buffering
// (libc by default uses bufferring except its own console/TTYs such as for pipes)
// we do have our own buffering so we disable stdlib io level bufferring
setvbuf(stdout, nil, _IONBF, 0) // disable buffering for stdout
setvbuf(stderr, nil, _IONBF, 0) // disable buffering for stderr
// Setup readability handlers for raw data
setupReadabilityHandler(for: outputHandle, isError: false)
setupReadabilityHandler(for: errorHandle, isError: true)
}
let shutdownLock = NSLock()
private func setupReadabilityHandler(for handle: FileHandle?, isError: Bool) {
handle?.readabilityHandler = { [weak self] handle in
let data = handle.availableData
if !data.isEmpty {
self?.writeQueue.async {
try? self?.writeData(data)
}
// Forward to original std stream
if let originalFD = isError ? self?.originalStderr : self?.originalStdout {
data.withUnsafeBytes { (bufferPointer) -> Void in
if let baseAddress = bufferPointer.baseAddress, bufferPointer.count > 0 {
write(originalFD, baseAddress, bufferPointer.count)
}
handle?.readabilityHandler = readHandler(isError: isError)
}
private func readHandler(isError: Bool) -> (FileHandle) -> Void {
return { [weak self] _ in
// Lock first before touching anything
self?.shutdownLock.lock()
defer { self?.shutdownLock.unlock() }
// Capture strong self *after* lock is acquired
guard let self = self else { return }
let handle = isError ? self.errorHandle : self.outputHandle
guard let data = handle?.availableData else { return }
writeQueue.async {
try? self.writeData(data)
}
// 2. Echo to original stdout/stderr if still valid
guard let fd = isError ? self.originalStderr : self.originalStdout else {
return
}
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "UnknownApp"
guard fcntl(fd, F_GETFD) != -1 else {
NSLog("[%@] ConsoleLogger: Original FD (%d) is invalid, skipping echo", appName, fd)
return
}
data.withUnsafeBytes { rawBufferPointer in
guard let base = rawBufferPointer.baseAddress else { return }
var remaining = data.count
var offset = 0
let maxChunkSize = 16 * 1024 // 16 KB chunks
// write in chunks, else will throw 'Result too large'
while remaining > 0 {
let chunkSize = min(maxChunkSize, remaining)
let written = write(fd, base.advanced(by: offset), chunkSize)
if written < 0 {
NSLog("[%@] ConsoleLogger: Failed to re-echo to FD %d: %s", appName, fd, strerror(errno))
break
}
remaining -= written
offset += written
}
}
}
}
func writeData(_ data: Data) throws {
throw AbstractClassError.abstractMethodInvoked
}
func stopCapturing() {
shutdownLock.lock()
defer { shutdownLock.unlock() }
ostream.close()
// Restore original stdout and stderr
if let stdout = originalStdout {
if let stdout = originalStdout, stdout != STDOUT_FILENO {
dup2(stdout, STDOUT_FILENO)
close(stdout)
}
if let stderr = originalStderr {
if let stderr = originalStderr, stderr != STDERR_FILENO {
dup2(stderr, STDERR_FILENO)
close(stderr)
}