Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fix document picker when sideloaded #33

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ jobs:

- name: Inject tweak and extension
run: |
cyan -duws -i discord.ipa -o ${{ env.APP_NAME }}.ipa -f ${{ env.DEB_FILE_NAME }} OpenInDiscord/build/OpenInDiscord.appex
cyan -duwsgq -i discord.ipa -o ${{ env.APP_NAME }}.ipa -f ${{ env.DEB_FILE_NAME }} OpenInDiscord/build/OpenInDiscord.appex

- name: Upload ipa as artifact
uses: actions/upload-artifact@v4
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ xcuserdata/
.theos/
packages/
.DS_Store

venv/
patcher
ipa-icons.zip
*.ipa
OpenInDiscord/
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"label": "$(cloud-upload) AirDrop Tweak",
"task": "AirDrop Tweak",
"tooltip": "AirDrop Tweak"
},
{
"label": "$(package) Build IPA",
"task": "Build IPA",
"tooltip": "Build IPA locally"
}
],
"externalFormatters.languages": {
Expand Down
11 changes: 11 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@
"close": true
},
"dependsOn": ["Build Tweak"]
},
{
"type": "shell",
"label": "Build IPA",
"command": "chmod +x build-local.sh && ./build-local.sh",
"presentation": {
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"reveal": "always"
}
}
]
}
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ To resolve the fixable issues, you need to match the app's bundle ID with your p
<td>✓</td>
<td rowspan="5"><img src="https://adriancastro.dev/e0hbonxknepw.jpg" width="300"></td>
</tr>
<tr>
<td>Cannot select items via Files app</td>
<td>✓</td>
</tr>
<tr>
<td>Cannot share items to Discord</td>
<td>✗</td>
Expand All @@ -45,10 +41,6 @@ To resolve the fixable issues, you need to match the app's bundle ID with your p
<td>Cannot use passkeys</td>
<td>✗</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>

## Doing this will break notifications if the app is backgrounded or closed
Expand Down
97 changes: 46 additions & 51 deletions Sources/Sideloading.x
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <UIKit/UIKit.h>
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#import <dlfcn.h>

typedef struct __CMSDecoder *CMSDecoderRef;
Expand All @@ -18,7 +19,6 @@ extern OSStatus CMSDecoderCopyContent(CMSDecoderRef cmsDecoder, CFDataRef *conte
#define DISCORD_NAME @"Discord"

typedef NS_ENUM(NSInteger, BundleIDError) {
BundleIDErrorFiles,
BundleIDErrorIcon,
BundleIDErrorPasskey
};
Expand All @@ -29,11 +29,10 @@ static void showBundleIDError(BundleIDError error) {
void (^completion)(void) = nil;

switch (error) {
case BundleIDErrorFiles:
case BundleIDErrorIcon:
message = @"For this to work change the Bundle ID so that it matches your "
@"provisioning profile's App ID (excluding the Team ID prefix).";
title = error == BundleIDErrorFiles ? @"Cannot Access Files" : @"Cannot Change Icon";
title = @"Cannot Change Icon";
break;
case BundleIDErrorPasskey:
message = @"Passkeys are not supported when sideloading Discord. "
Expand All @@ -46,41 +45,6 @@ static void showBundleIDError(BundleIDError error) {
showErrorAlert(title, message, completion);
}

static NSString *getProvisioningAppID(void) {
NSString *provisionPath = [NSBundle.mainBundle pathForResource:@"embedded"
ofType:@"mobileprovision"];
if (!provisionPath)
return nil;
NSData *provisionData = [NSData dataWithContentsOfFile:provisionPath];
if (!provisionData)
return nil;
CMSDecoderRef decoder = NULL;
CMSDecoderCreate(&decoder);
CMSDecoderUpdateMessage(decoder, provisionData.bytes, provisionData.length);
CMSDecoderFinalizeMessage(decoder);
CFDataRef dataRef = NULL;
CMSDecoderCopyContent(decoder, &dataRef);
NSData *data = (__bridge_transfer NSData *)dataRef;
if (decoder)
CFRelease(decoder);
NSError *error = nil;
id plist = [NSPropertyListSerialization propertyListWithData:data
options:0
format:NULL
error:&error];
if (!plist || ![plist isKindOfClass:[NSDictionary class]])
return nil;
NSString *appID = plist[@"Entitlements"][@"application-identifier"];
if (!appID)
return nil;
NSArray *components = [appID componentsSeparatedByString:@"."];
if (components.count > 1) {
return [[components subarrayWithRange:NSMakeRange(1, components.count - 1)]
componentsJoinedByString:@"."];
}
return nil;
}

static NSString *getAccessGroupID(void) {
NSDictionary *query = @{
(__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword,
Expand Down Expand Up @@ -180,22 +144,53 @@ static BOOL isSelfCall(void) {
}
%end

%hook UIViewController
- (void)presentViewController:(UIViewController *)viewControllerToPresent
animated:(BOOL)flag
completion:(void (^)(void))completion {
if ([viewControllerToPresent isKindOfClass:[UIDocumentPickerViewController class]]) {
NSString *provisioningAppID = getProvisioningAppID();
NSString *currentBundleID = [[NSBundle mainBundle] bundleIdentifier];

if (provisioningAppID && ![provisioningAppID isEqualToString:currentBundleID]) {
BunnyLog(@"Intercepted UIDocumentPickerViewController presentation");
showBundleIDError(BundleIDErrorFiles);
return;
}
// https://github.com/khanhduytran0/LiveContainer/blob/main/TweakLoader/DocumentPicker.m
%hook UIDocumentPickerViewController

- (instancetype)initForOpeningContentTypes:(NSArray<UTType *> *)contentTypes asCopy:(BOOL)asCopy {
BOOL shouldMultiselect = NO;
if ([contentTypes count] == 1 && contentTypes[0] == UTTypeFolder) {
shouldMultiselect = YES;
}

NSArray<UTType *> *contentTypesNew = @[ UTTypeItem, UTTypeFolder ];

UIDocumentPickerViewController *ans = %orig(contentTypesNew, YES);
if (shouldMultiselect) {
[ans setAllowsMultipleSelection:YES];
}
return ans;
}

- (instancetype)initWithDocumentTypes:(NSArray<UTType *> *)contentTypes inMode:(NSUInteger)mode {
return [self initForOpeningContentTypes:contentTypes asCopy:(mode == 1 ? NO : YES)];
}

- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection {
if ([self allowsMultipleSelection]) {
return;
}
%orig(YES);
}

%end

%hook UIDocumentBrowserViewController

- (instancetype)initForOpeningContentTypes:(NSArray<UTType *> *)contentTypes {
NSArray<UTType *> *contentTypesNew = @[ UTTypeItem, UTTypeFolder ];
return %orig(contentTypesNew);
}

%end

%hook NSURL

- (BOOL)startAccessingSecurityScopedResource {
%orig;
return YES;
}

%end

%hook ASAuthorizationController
Expand Down
143 changes: 143 additions & 0 deletions build-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/bin/bash

RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'

print_status() {
echo -e "${BLUE}[*]${NC} $1"
}

print_success() {
echo -e "${GREEN}[+]${NC} $1"
}

print_error() {
echo -e "${RED}[-]${NC} $1"
}

IPA_FILE=$(find . -maxdepth 1 -name "*.ipa" -print -quit)

if [ -z "$IPA_FILE" ]; then
print_status "No IPA found. Please enter Discord IPA URL:"
read DISCORD_URL

if [ -z "$DISCORD_URL" ]; then
print_error "No URL provided"
exit 1
fi

print_status "Downloading Discord IPA..."
curl -L -o discord.ipa "$DISCORD_URL"

if [ $? -ne 0 ]; then
print_error "Failed to download Discord IPA"
exit 1
fi
IPA_FILE="discord.ipa"
print_success "Downloaded Discord IPA"
fi

print_status "Building tweak..."
make package FINALPACKAGE=1

if [ $? -ne 0 ]; then
print_error "Failed to build tweak"
exit 1
fi
print_success "Built tweak"

print_status "Downloading IPA icons..."
curl -L -o ipa-icons.zip https://raw.githubusercontent.com/pyoncord/assets/main/ipa-icons.zip

if [ $? -ne 0 ]; then
print_error "Failed to download IPA icons"
exit 1
fi
print_success "Downloaded IPA icons"

print_status "Downloading patcher..."
curl -L -o patcher https://github.com/amsyarasyiq/bunny-ipa-patcher/releases/download/release-pyon/patcher.mac-amd64
chmod +x patcher

if [ $? -ne 0 ]; then
print_error "Failed to download patcher"
exit 1
fi
print_success "Downloaded patcher"

print_status "Cloning Safari extension..."
rm -rf OpenInDiscord
git clone https://github.com/castdrian/OpenInDiscord

if [ $? -ne 0 ]; then
print_error "Failed to clone Safari extension"
exit 1
fi
print_success "Cloned Safari extension"

print_status "Building Safari extension..."
cd OpenInDiscord
xcodebuild build \
-target "OpenInDiscord Extension" \
-configuration Release \
-sdk iphoneos \
CONFIGURATION_BUILD_DIR="build" \
PRODUCT_NAME="OpenInDiscord" \
PRODUCT_BUNDLE_IDENTIFIER="com.hammerandchisel.discord.OpenInDiscord" \
PRODUCT_MODULE_NAME="OpenInDiscordExt" \
SKIP_INSTALL=NO \
DEVELOPMENT_TEAM="" \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
ONLY_ACTIVE_ARCH=NO
cd ..

if [ $? -ne 0 ]; then
print_error "Failed to build Safari extension"
exit 1
fi
print_success "Built Safari extension"

print_status "Patching IPA..."
./patcher -d "$IPA_FILE" -o discord-patched.ipa -i ./ipa-icons.zip

if [ $? -ne 0 ]; then
print_error "Failed to patch IPA"
exit 1
fi
print_success "Patched IPA"

print_status "Setting up Python environment..."
python3 -m venv venv
source venv/bin/activate
pip install --force-reinstall https://github.com/asdfzxcvbn/pyzule-rw/archive/main.zip Pillow

if [ $? -ne 0 ]; then
print_error "Failed to install cyan"
exit 1
fi
print_success "Installed cyan"

NAME=$(grep '^Name:' control | cut -d ' ' -f 2)
PACKAGE=$(grep '^Package:' control | cut -d ' ' -f 2)
VERSION=$(grep '^Version:' control | cut -d ' ' -f 2)
DEB_FILE="packages/${PACKAGE}_${VERSION}_iphoneos-arm.deb"

print_status "Injecting tweak..."
cyan -duwsgq -i discord-patched.ipa -o "$NAME.ipa" -f "$DEB_FILE" OpenInDiscord/build/OpenInDiscord.appex

if [ $? -ne 0 ]; then
print_error "Failed to inject tweak"
exit 1
fi

deactivate
print_success "Successfully created $NAME.ipa"

print_status "Cleaning up..."
rm -f patcher ipa-icons.zip discord-patched.ipa

print_success "Successfully created $NAME.ipa"
2 changes: 1 addition & 1 deletion control
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: app.pyoncord
Name: Bunny
Version: 0.6.0
Version: 0.7.0
Architecture: iphoneos-arm
Description: A client modification for Discord's mobile app.
Maintainer: Adrian Castro, Pylix
Expand Down