Skip to content

Commit

Permalink
Merge pull request #33 from bunny-mod/feat-file-picker-fix
Browse files Browse the repository at this point in the history
feat: fix document picker when sideloaded
  • Loading branch information
castdrian authored Dec 2, 2024
2 parents 10ccd59 + 072a2a7 commit c43a379
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 61 deletions.
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

0 comments on commit c43a379

Please sign in to comment.