[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report-issue-template.yml",
    "content": "name: Bug Report\ndescription: Bug Report (No Duplicates Issue Here!)\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        # Before post issue please read me\n        Your issue may already be reported!\n        Please search on the [issue tracker](https://github.com/opa334/TrollStore/issues) before creating one.\n\n  - type: checkboxes\n    id: no-duplicates-issue\n    attributes:\n      label: No Duplicates Issue\n      options:\n        - label:  I'm sure I've searched on the issue tracker before creating one.\n          required: true\n\n  - type: textarea\n    id: expected-behavior\n    attributes:\n      label: Expected Behavior?\n      description: |\n         Describing a bug, tell us what should happen\n      placeholder: Tell us what you **Expected**!\n    validations:\n      required: true\n\n  - type: textarea\n    id: current-behavior\n    attributes:\n      label: Current Behavior?\n      description: |\n         Describing a bug, tell us what happens instead of the expected behavior\n      placeholder: Tell us what you **Happened**!\n    validations:\n      required: true\n\n  - type: textarea\n    id: possible-solution\n    attributes:\n      label: Possible Solution?\n      description: |\n         Not obligatory, but suggest a fix/reason for the bug,\n         or ideas how to implement the addition or change\n      placeholder: If not sure leave blank.\n      value: \"-\"\n\n  - type: textarea\n    id: steps-to-reproduce\n    attributes:\n      label: Steps to Reproduce\n      description: |\n         Provide a link to a live example, or an unambiguous set of steps to\n         reproduce this bug. Include code to reproduce, if relevant\n      placeholder: |\n        1.\n        2.\n        3.\n      value: \"-\"\n\n  - type: markdown\n    attributes:\n      value: |\n        # Your Environment\n\n  - type: input\n    id: trollstore-version\n    attributes:\n      label: TrollStore Version\n      description: like TrollInstaller2\n    validations:\n      required: true\n\n  - type: input\n    id: ios-version\n    attributes:\n      label: iOS/iPadOS version\n      description: like iOS 15.1\n    validations:\n      required: true\n      \n  - type: input\n    id: idevice-model\n    attributes:\n      label: iDevice Model\n      description: like iPhone 11\n    validations:\n      required: true\n\n  - type: input\n    id: other-env\n    attributes:\n      label: Other info of your environment\n      description: like macOS 12.6, the newest THEOS...\n\n"
  },
  {
    "path": ".gitignore",
    "content": "out/\n.DS_Store\n.theos/\npackages/\nxcuserdata\n.vscode\nPwnify/pwnify\nInstallerVictim.ipa\n_build"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"ChOma\"]\n\tpath = ChOma\n\turl = https://github.com/opa334/ChOma\n"
  },
  {
    "path": "LICENSE",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: TrollStore\nUpstream-Contact: opa334 <opa334@protonmail.com>\nSource: https://github.com/opa334/TrollStore\n\nFiles: *\nCopyright: 2022-2024 Lars Fröder\nLicense: MIT\n\nFiles: RootHelper/uicache.m\nCopyright: Copyright (c) 2019 CoolStar,\n           Modified work Copyright (c) 2020-2022 Procursus Team <team@procurs.us>\n           Modified work Copyright (c) 2022-2024 Lars Fröder <opa334@protonmail.com>\nLicense: BSD-4-Clause\n\nLicense: BSD-4-Clause\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions\n are met:\n 1. Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n 2. Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in the\n    documentation and/or other materials provided with the distribution.\n 3. All advertising materials mentioning features or use of this software\n    must display the following acknowledgement:\n      This product includes software developed by CoolStar.\n 4. Neither the name of the copyright holder nor the names of its contributors\n    may be used to endorse or promote products derived from this software\n    without specific prior written permission.\n .\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE HOLDERS OR\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nLicense: MIT\n Permission is hereby granted, free of charge, to any person obtaining a\n copy of this software and associated documentation files (the \"Software\"),\n to deal in the Software without restriction, including without limitation\n the rights to use, copy, modify, merge, publish, distribute, sublicense,\n and/or sell copies of the Software, and to permit persons to whom the\n Software is furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included\n in all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "Makefile",
    "content": "TOPTARGETS := all clean update\n\n$(TOPTARGETS): pre_build make_fastPathSign make_roothelper make_trollstore make_trollhelper_embedded make_trollhelper_package assemble_trollstore build_installer15 build_installer64e make_trollstore_lite\n\npre_build:\n\t@rm -rf ./_build 2>/dev/null || true\n\t@mkdir -p ./_build\n\nmake_fastPathSign:\n\t@$(MAKE) -C ./Exploits/fastPathSign $(MAKECMDGOALS)\n\nmake_roothelper:\n\t@$(MAKE) -C ./RootHelper DEBUG=0 $(MAKECMDGOALS)\n\nmake_trollstore:\n\t@$(MAKE) -C ./TrollStore FINALPACKAGE=1 $(MAKECMDGOALS)\n\nifneq ($(MAKECMDGOALS),clean)\n\nmake_trollhelper_package:\n\t@$(MAKE) clean -C ./TrollHelper\n\t@cp ./RootHelper/.theos/obj/trollstorehelper ./TrollHelper/Resources/trollstorehelper\n\t@$(MAKE) -C ./TrollHelper FINALPACKAGE=1 package $(MAKECMDGOALS)\n\t@$(MAKE) clean -C ./TrollHelper\n\t@$(MAKE) -C ./TrollHelper THEOS_PACKAGE_SCHEME=rootless FINALPACKAGE=1 package $(MAKECMDGOALS)\n\t@rm ./TrollHelper/Resources/trollstorehelper\n\nmake_trollhelper_embedded:\n\t@$(MAKE) clean -C ./TrollHelper\n\t@$(MAKE) -C ./TrollHelper FINALPACKAGE=1 EMBEDDED_ROOT_HELPER=1 $(MAKECMDGOALS)\n\t@cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./_build/PersistenceHelper_Embedded\n\t@$(MAKE) clean -C ./TrollHelper\n\t@$(MAKE) -C ./TrollHelper FINALPACKAGE=1 EMBEDDED_ROOT_HELPER=1 LEGACY_CT_BUG=1 $(MAKECMDGOALS)\n\t@cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./_build/PersistenceHelper_Embedded_Legacy_arm64\n\t@$(MAKE) clean -C ./TrollHelper\n\t@$(MAKE) -C ./TrollHelper FINALPACKAGE=1 EMBEDDED_ROOT_HELPER=1 CUSTOM_ARCHS=arm64e $(MAKECMDGOALS)\n\t@cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./_build/PersistenceHelper_Embedded_Legacy_arm64e\n\t@$(MAKE) clean -C ./TrollHelper\n\nassemble_trollstore:\n\t@cp ./RootHelper/.theos/obj/trollstorehelper ./TrollStore/.theos/obj/TrollStore.app/trollstorehelper\n\t@cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./TrollStore/.theos/obj/TrollStore.app/PersistenceHelper\n\t@export COPYFILE_DISABLE=1\n\t@tar -czvf ./_build/TrollStore.tar -C ./TrollStore/.theos/obj TrollStore.app\n\nbuild_installer15:\n\t@mkdir -p ./_build/tmp15\n\t@unzip ./Victim/InstallerVictim.ipa -d ./_build/tmp15\n\t@cp ./_build/PersistenceHelper_Embedded_Legacy_arm64 ./_build/TrollStorePersistenceHelperToInject\n\t@pwnify set-cpusubtype ./_build/TrollStorePersistenceHelperToInject 1\n\t@ldid -s -K./Victim/victim.p12 ./_build/TrollStorePersistenceHelperToInject\n\tAPP_PATH=$$(find ./_build/tmp15/Payload -name \"*\" -depth 1) ; \\\n\tAPP_NAME=$$(basename $$APP_PATH) ; \\\n\tBINARY_NAME=$$(echo \"$$APP_NAME\" | cut -f 1 -d '.') ; \\\n\techo $$BINARY_NAME ; \\\n\tpwnify pwn ./_build/tmp15/Payload/$$APP_NAME/$$BINARY_NAME ./_build/TrollStorePersistenceHelperToInject\n\t@pushd ./_build/tmp15 ; \\\n\tzip -vrD ../../_build/TrollHelper_iOS15.ipa * ; \\\n\tpopd\n\t@rm ./_build/TrollStorePersistenceHelperToInject\n\t@rm -rf ./_build/tmp15\n\nbuild_installer64e:\n\t@mkdir -p ./_build/tmp64e\n\t@unzip ./Victim/InstallerVictim.ipa -d ./_build/tmp64e\n\tAPP_PATH=$$(find ./_build/tmp64e/Payload -name \"*\" -depth 1) ; \\\n\tAPP_NAME=$$(basename $$APP_PATH) ; \\\n\tBINARY_NAME=$$(echo \"$$APP_NAME\" | cut -f 1 -d '.') ; \\\n\techo $$BINARY_NAME ; \\\n\tpwnify pwn64e ./_build/tmp64e/Payload/$$APP_NAME/$$BINARY_NAME ./_build/PersistenceHelper_Embedded_Legacy_arm64e\n\t@pushd ./_build/tmp64e ; \\\n\tzip -vrD ../../_build/TrollHelper_arm64e.ipa * ; \\\n\tpopd\n\t@rm -rf ./_build/tmp64e\n\nmake_trollstore_lite:\n\t@$(MAKE) -C ./RootHelper DEBUG=0 TROLLSTORE_LITE=1\n\t@rm -rf ./TrollStoreLite/Resources/trollstorehelper\n\t@cp ./RootHelper/.theos/obj/trollstorehelper_lite ./TrollStoreLite/Resources/trollstorehelper\n\t@$(MAKE) -C ./TrollStoreLite package FINALPACKAGE=1\n\t@$(MAKE) -C ./RootHelper TROLLSTORE_LITE=1 clean\n\t@$(MAKE) -C ./TrollStoreLite clean\n\t@$(MAKE) -C ./RootHelper DEBUG=0 TROLLSTORE_LITE=1 THEOS_PACKAGE_SCHEME=rootless\n\t@rm -rf ./TrollStoreLite/Resources/trollstorehelper\n\t@cp ./RootHelper/.theos/obj/trollstorehelper_lite ./TrollStoreLite/Resources/trollstorehelper\n\t@$(MAKE) -C ./TrollStoreLite package FINALPACKAGE=1 THEOS_PACKAGE_SCHEME=rootless\n\nelse\nmake_trollstore_lite:\n\t@$(MAKE) -C ./TrollStoreLite $(MAKECMDGOALS)\nendif\n\n.PHONY: $(TOPTARGETS) pre_build assemble_trollstore make_trollhelper_package make_trollhelper_embedded build_installer15 build_installer64e"
  },
  {
    "path": "Pwnify/Makefile",
    "content": "pwnify:\n\t@clang main.m -fobjc-arc -fmodules -mmacosx-version-min=11.0 -o pwnify\n\ninstall: pwnify\n\t-@sudo rm /usr/local/bin/pwnify 2>/dev/null || true\n\t@sudo cp ./pwnify /usr/local/bin/pwnify\n\nclean:\n\t@rm ./pwnify 2>/dev/null || true"
  },
  {
    "path": "Pwnify/main.m",
    "content": "//\n//  main.m\n//  pwnify-universal\n//\n//  Created by Lars Fröder on 08.10.22.\n//\n\n#import <Foundation/Foundation.h>\n\n#import <mach-o/loader.h>\n#import <mach-o/fat.h>\n#import <sys/stat.h>\n\n#define ALIGN_DEFAULT 0xE\n\nuint32_t roundUp(int numToRound, int multiple)\n{\n\tif (multiple == 0)\n\t\treturn numToRound;\n\n\tint remainder = numToRound % multiple;\n\tif (remainder == 0)\n\t\treturn numToRound;\n\n\treturn numToRound + multiple - remainder;\n}\n\nvoid expandFile(FILE* file, uint32_t size)\n{\n\tfseek(file, 0, SEEK_END);\n\tif(ftell(file) >= size) return;\n\t\n\twhile(ftell(file) != size)\n\t{\n\t\tchar c = 0;\n\t\tfwrite(&c, 1, 1, file);\n\t}\n}\n\nvoid copyData(FILE* sourceFile, FILE* targetFile, size_t size)\n{\n\tfor(size_t i = 0; i < size; i++)\n\t{\n\t\tchar b;\n\t\tfread(&b, 1, 1, sourceFile);\n\t\tfwrite(&b, 1, 1, targetFile);\n\t}\n}\n\nvoid enumerateArchs(NSString* binaryPath, void (^archEnumBlock)(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop))\n{\n\tFILE* machoFile = fopen(binaryPath.fileSystemRepresentation, \"rb\");\n\tif(!machoFile) return;\n\t\n\tstruct mach_header header;\n\tfread(&header,sizeof(header),1,machoFile);\n\t\n\tif(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM)\n\t{\n\t\tfseek(machoFile,0,SEEK_SET);\n\t\tstruct fat_header fatHeader;\n\t\tfread(&fatHeader,sizeof(fatHeader),1,machoFile);\n\t\t\n\t\tfor(int i = 0; i < OSSwapBigToHostInt32(fatHeader.nfat_arch); i++)\n\t\t{\n\t\t\tuint32_t archFileOffset = sizeof(fatHeader) + sizeof(struct fat_arch) * i;\n\t\t\tstruct fat_arch fatArch;\n\t\t\tfseek(machoFile, archFileOffset,SEEK_SET);\n\t\t\tfread(&fatArch,sizeof(fatArch),1,machoFile);\n\t\t\t\n\t\t\tuint32_t sliceFileOffset = OSSwapBigToHostInt32(fatArch.offset);\n\t\t\tstruct mach_header archHeader;\n\t\t\tfseek(machoFile, sliceFileOffset, SEEK_SET);\n\t\t\tfread(&archHeader,sizeof(archHeader),1,machoFile);\n\t\t\t\n\t\t\tBOOL stop = NO;\n\t\t\tarchEnumBlock(&fatArch, archFileOffset, &archHeader, sliceFileOffset, machoFile, &stop);\n\t\t\tif(stop) break;\n\t\t}\n\t}\n\telse if(header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64)\n\t{\n\t\tBOOL stop;\n\t\tarchEnumBlock(NULL, 0, &header, 0, machoFile, &stop);\n\t}\n\t\n\tfclose(machoFile);\n}\n\nvoid printArchs(NSString* binaryPath)\n{\n\t__block int i = 0;\n\tenumerateArchs(binaryPath, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {\n\t\tif(arch)\n\t\t{\n\t\t\tprintf(\"%d. fatArch type: 0x%X, subtype: 0x%X, align:0x%X, size:0x%X, offset:0x%X\\n| \", i, OSSwapBigToHostInt32(arch->cputype), OSSwapBigToHostInt32(arch->cpusubtype), OSSwapBigToHostInt32(arch->align), OSSwapBigToHostInt32(arch->size), OSSwapBigToHostInt32(arch->offset));\n\t\t}\n\t\tprintf(\"machHeader type: 0x%X, subtype: 0x%X\\n\", OSSwapLittleToHostInt32(machHeader->cputype), OSSwapLittleToHostInt32(machHeader->cpusubtype));\n\n\t\ti++;\n\t});\n}\n\nvoid pwnify(NSString* appStoreBinary, NSString* binaryToInject, BOOL preferArm64e)\n{\n\tNSString* tmpFilePath = [NSTemporaryDirectory() stringByAppendingString:[[NSUUID UUID] UUIDString]];\n\t\n\t// Determine amount of slices in output\n\t__block int slicesCount = 1;\n\tenumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {\n\t\tslicesCount++;\n\t});\n\t\n\t// Allocate FAT data\n\tuint32_t fatDataSize = sizeof(struct fat_header) + slicesCount * sizeof(struct fat_arch);\n\tchar* fatData = malloc(fatDataSize);\n\n\t// Construct new fat header\n\tstruct fat_header fatHeader;\n\tfatHeader.magic = OSSwapHostToBigInt32(0xCAFEBABE);\n\tfatHeader.nfat_arch = OSSwapHostToBigInt32(slicesCount);\n\tmemcpy(&fatData[0], &fatHeader, sizeof(fatHeader));\n\t\n\tuint32_t align = pow(2, ALIGN_DEFAULT);\n\t__block uint32_t curOffset = align;\n\t__block uint32_t curArchIndex = 0;\n\n\t// Construct new fat arch data\n\tenumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {\n\t\tstruct fat_arch newArch;\n\t\tif(arch)\n\t\t{\n\t\t\tnewArch.cputype = arch->cputype;\n\t\t\t\n\t\t\tif(OSSwapBigToHostInt32(arch->cputype) == 0x100000C)\n\t\t\t{\n\t\t\t\tnewArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewArch.cpusubtype = arch->cpusubtype;\n\t\t\t}\n\t\t\t\n\t\t\tnewArch.size = arch->size;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype));\n\t\t\t\n\t\t\tif(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)\n\t\t\t{\n\t\t\t\tnewArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype));\n\t\t\t}\n\t\t\t\n\t\t\tnewArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:appStoreBinary error:nil] fileSize]);\n\t\t}\n\t\t\n\t\tnewArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT);\n\t\tnewArch.offset = OSSwapHostToBigInt32(curOffset);\n\t\tcurOffset += roundUp(OSSwapBigToHostInt32(newArch.size), align);\n\t\t\n\t\tmemcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], &newArch, sizeof(newArch));\n\t\tcurArchIndex++;\n\t});\n\t\n\t// Determine what slices our injection binary contains\n\t__block BOOL toInjectHasArm64e = NO;\n\t__block BOOL toInjectHasArm64 = NO;\n\tenumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {\n\t\tif(arch)\n\t\t{\n\t\t\tif(OSSwapBigToHostInt32(arch->cputype) == 0x100000C)\n\t\t\t{\n\t\t\t\tif (!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x2) & 0xFFFFFF))\n\t\t\t\t{\n\t\t\t\t\ttoInjectHasArm64e = YES;\n\t\t\t\t}\n\t\t\t\telse if(!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x1) & 0xFFFFFF))\n\t\t\t\t{\n\t\t\t\t\ttoInjectHasArm64 = YES;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)\n\t\t\t{\n\t\t\t\tif (!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x2) & 0xFFFFFF))\n\t\t\t\t{\n\t\t\t\t\ttoInjectHasArm64e = YES;\n\t\t\t\t}\n\t\t\t\telse if(!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x1) & 0xFFFFFF))\n\t\t\t\t{\n\t\t\t\t\ttoInjectHasArm64 = YES;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\t\n\tif(!toInjectHasArm64 && !preferArm64e)\n\t{\n\t\tprintf(\"ERROR: can't proceed injection because binary to inject has no arm64 slice\\n\");\n\t\treturn;\n\t}\n\t\n\tuint32_t subtypeToUse = 0x1;\n\tif(preferArm64e && toInjectHasArm64e)\n\t{\n\t\tsubtypeToUse = 0x2;\n\t}\n\t\n\tenumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {\n\t\tstruct fat_arch currentArch;\n\t\tif(arch)\n\t\t{\n\t\t\tcurrentArch.cputype = arch->cputype;\n\t\t\tcurrentArch.cpusubtype = arch->cpusubtype;\n\t\t\tcurrentArch.size = arch->size;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcurrentArch.cputype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cputype));\n\t\t\tcurrentArch.cpusubtype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cpusubtype));\n\t\t\tcurrentArch.size = OSSwapHostToBigInt((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]);\n\t\t}\n\n\t\tif(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C)\n\t\t{\n\t\t\tif (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF))\n\t\t\t{\n\t\t\t\tcurrentArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT);\n\t\t\t\tcurrentArch.offset = OSSwapHostToBigInt32(curOffset);\n\t\t\t\tcurOffset += roundUp(OSSwapBigToHostInt32(currentArch.size), align);\n\t\t\t\tmemcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], &currentArch, sizeof(currentArch));\n\t\t\t\tcurArchIndex++;\n\t\t\t\t*stop = YES;\n\t\t\t}\n\t\t}\n\t});\n\t\n\t// FAT Header constructed, now write to file and then write the slices themselves\n\t\n\tFILE* tmpFile = fopen(tmpFilePath.fileSystemRepresentation, \"wb\");\n\tfwrite(&fatData[0], fatDataSize, 1, tmpFile);\n\t\n\tcurArchIndex = 0;\n\tenumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {\n\t\tstruct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex];\n\t\t\n\t\texpandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset));\n\t\t\n\t\tuint32_t offset = 0;\n\t\tuint32_t size = 0;\n\t\t\n\t\tif(arch)\n\t\t{\n\t\t\toffset = OSSwapBigToHostInt32(arch->offset);\n\t\t\tsize = OSSwapBigToHostInt32(arch->size);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsize = OSSwapBigToHostInt32(toWriteArch->size);\n\t\t}\n\t\t\n\t\tFILE* appStoreBinaryFile = fopen(appStoreBinary.fileSystemRepresentation, \"rb\");\n\t\tfseek(appStoreBinaryFile, offset, SEEK_SET);\n\t\tcopyData(appStoreBinaryFile, tmpFile, size);\n\t\tfclose(appStoreBinaryFile);\n\t\tcurArchIndex++;\n\t});\n\t\n\tstruct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex];\n\tenumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {\n\t\tstruct fat_arch currentArch;\n\t\tif(arch)\n\t\t{\n\t\t\tcurrentArch.cputype = arch->cputype;\n\t\t\tcurrentArch.cpusubtype = arch->cpusubtype;\n\t\t\tcurrentArch.size = arch->size;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcurrentArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype));\n\t\t\tcurrentArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype));\n\t\t\tcurrentArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]);\n\t\t}\n\n\t\tif(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C)\n\t\t{\n\t\t\tif (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF))\n\t\t\t{\n\t\t\t\texpandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset));\n\t\t\t\t\n\t\t\t\tuint32_t offset = 0;\n\t\t\t\tuint32_t size = 0;\n\t\t\t\t\n\t\t\t\tif(arch)\n\t\t\t\t{\n\t\t\t\t\toffset = OSSwapBigToHostInt32(arch->offset);\n\t\t\t\t\tsize = OSSwapBigToHostInt32(arch->size);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsize = OSSwapBigToHostInt32(toWriteArch->size);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tFILE* binaryToInjectFile = fopen(binaryToInject.fileSystemRepresentation, \"rb\");\n\t\t\t\tfseek(binaryToInjectFile, offset, SEEK_SET);\n\t\t\t\tcopyData(binaryToInjectFile, tmpFile, size);\n\t\t\t\tfclose(binaryToInjectFile);\n\t\t\t\t*stop = YES;\n\t\t\t}\n\t\t}\n\t});\n\t\n\tfclose(tmpFile);\n\tchmod(tmpFilePath.fileSystemRepresentation, 0755);\n\t\n\t[[NSFileManager defaultManager] removeItemAtPath:appStoreBinary error:nil];\n\t[[NSFileManager defaultManager] moveItemAtPath:tmpFilePath toPath:appStoreBinary error:nil];\n}\n\nvoid setCPUSubtype(NSString* binaryPath, uint32_t subtype)\n{\n\tFILE* binaryFile = fopen(binaryPath.fileSystemRepresentation, \"rb+\");\n\tif(!binaryFile)\n\t{\n\t\tprintf(\"ERROR: File not found\\n\");\n\t\treturn;\n\t}\n\t\n\tenumerateArchs(binaryPath, ^(struct fat_arch *arch, uint32_t archFileOffset, struct mach_header *machHeader, uint32_t sliceFileOffset, FILE *file, BOOL *stop) {\n\t\t\n\t\tif(arch)\n\t\t{\n\t\t\tif(OSSwapBigToHostInt(arch->cputype) == 0x100000C)\n\t\t\t{\n\t\t\t\tif(OSSwapBigToHostInt(arch->cpusubtype) == 0x0)\n\t\t\t\t{\n\t\t\t\t\tarch->cpusubtype = OSSwapHostToBigInt32(subtype);\n\t\t\t\t\tfseek(binaryFile, archFileOffset, SEEK_SET);\n\t\t\t\t\tfwrite(arch, sizeof(struct fat_arch), 1, binaryFile);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)\n\t\t{\n\t\t\tif(OSSwapLittleToHostInt32(machHeader->cpusubtype) == 0x0)\n\t\t\t{\n\t\t\t\tmachHeader->cpusubtype = OSSwapHostToLittleInt32(subtype);\n\t\t\t\tfseek(binaryFile, sliceFileOffset, SEEK_SET);\n\t\t\t\tfwrite(machHeader, sizeof(struct mach_header), 1, binaryFile);\n\t\t\t}\n\t\t}\n\t});\n\t\n\tfclose(binaryFile);\n}\n\nvoid printUsageAndExit(void)\n{\n\tprintf(\"Usage:\\n\\nPrint architectures of a binary:\\npwnify print <path/to/binary>\\n\\nInject target slice into victim binary:\\npwnify pwn(64e) <path/to/victim/binary> <path/to/target/binary>\\n\\nModify cpusubtype of a non FAT binary:\\npwnify set-cpusubtype <path/to/binary> <cpusubtype>\\n\");\n\texit(0);\n}\n\nint main(int argc, const char * argv[]) {\n\t@autoreleasepool {\n\t\tif(argc < 3)\n\t\t{\n\t\t\tprintUsageAndExit();\n\t\t}\n\t\t\n\t\tNSString* operation = [NSString stringWithUTF8String:argv[1]];\n\t\t\n\t\tif([operation isEqualToString:@\"print\"])\n\t\t{\n\t\t\tNSString* binaryToPrint = [NSString stringWithUTF8String:argv[2]];\n\t\t\tprintArchs(binaryToPrint);\n\t\t}\n\t\telse if([operation isEqualToString:@\"pwn\"])\n\t\t{\n\t\t\tif(argc < 4) printUsageAndExit();\n\t\t\tNSString* victimBinary = [NSString stringWithUTF8String:argv[2]];\n\t\t\tNSString* targetBinary = [NSString stringWithUTF8String:argv[3]];\n\t\t\tpwnify(victimBinary, targetBinary, NO);\n\t\t}\n\t\telse if([operation isEqualToString:@\"pwn64e\"])\n\t\t{\n\t\t\tif(argc < 4) printUsageAndExit();\n\t\t\tNSString* victimBinary = [NSString stringWithUTF8String:argv[2]];\n\t\t\tNSString* targetBinary = [NSString stringWithUTF8String:argv[3]];\n\t\t\tpwnify(victimBinary, targetBinary, YES);\n\t\t}\n\t\telse if([operation isEqualToString:@\"set-cpusubtype\"])\n\t\t{\n\t\t\tif(argc < 4) printUsageAndExit();\n\t\t\tNSString* binaryToModify = [NSString stringWithUTF8String:argv[2]];\n\t\t\tNSString* subtypeToSet = [NSString stringWithUTF8String:argv[3]];\n\t\t\t\n\t\t\tNSNumberFormatter* f = [[NSNumberFormatter alloc] init];\n\t\t\tf.numberStyle = NSNumberFormatterDecimalStyle;\n\t\t\tNSNumber* subtypeToSetNum = [f numberFromString:subtypeToSet];\n\n\t\t\tsetCPUSubtype(binaryToModify, [subtypeToSetNum unsignedIntValue]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintUsageAndExit();\n\t\t}\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "README.md",
    "content": "# TrollStore\n\nTrollStore is a permasigned jailed app that can permanently install any IPA you open in it.\n\nIt works because of an AMFI/CoreTrust bug where iOS does not correctly verify code signatures of binaries in which there are multiple signers.\n\nSupported versions: 14.0 beta 2 - 16.6.1, 16.7 RC (20H18), 17.0\n\n## Installing TrollStore\n\nFor installing TrollStore, refer to the guides at [ios.cfw.guide](https://ios.cfw.guide/installing-trollstore)\n\n16.7.x (excluding 16.7 RC) and 17.0.1+ will NEVER be supported (unless a third CoreTrust bug is discovered, which is unlikely).\n\n## Updating TrollStore\n\nWhen a new TrollStore update is available, a button to install it will appear at the top in the TrollStore settings. After tapping the button, TrollStore will automatically download the update, install it, and respring.\n\nAlternatively (if anything goes wrong), you can download the TrollStore.tar file under Releases and open it in TrollStore, TrollStore will install the update and respring.\n\n## Uninstalling an app\n\nApps installed from TrollStore can only be uninstalled from TrollStore itself, tap an app or swipe it to the left in the 'Apps' tab to delete it.\n\n## Persistence Helper\n\nThe CoreTrust bug used in TrollStore is only enough to install \"System\" apps, this is because FrontBoard has an additional security check (it calls libmis) every time before a user app is launched. Unfortunately it is not possible to install new \"System\" apps that stay through an icon cache reload. Therefore, when iOS reloads the icon cache, all TrollStore installed apps including TrollStore itself will revert back to \"User\" state and will no longer launch.\n\nThe only way to work around this is to install a persistence helper into a system app, this helper can then be used to reregister TrollStore and its installed apps as \"System\" so that they become launchable again, an option for this is available in TrollStore settings.\n\nOn jailbroken iOS 14 when TrollHelper is used for installation, it is located in /Applications and will persist as a \"System\" app through icon cache reloads, therefore TrollHelper is used as the persistence helper on iOS 14.\n\n## URL Scheme\n\nAs of version 1.3, TrollStore replaces the system URL scheme \"apple-magnifier\" (this is done so \"jailbreak\" detections can't detect TrollStore like they could if TrollStore had a unique URL scheme). This URL scheme can be used to install applications right from the browser, or to enable JIT from the app itself (only 2.0.12 and above), the format goes as follows:\n\n- `apple-magnifier://install?url=<URL_to_IPA>`\n- `apple-magnifier://enable-jit?bundle-id=<Bundle_ID>`\n\nOn devices that don't have TrollStore (1.3+) installed, this will just open the magnifier app.\n\n## Features\n\nThe binaries inside an IPA can have arbitrary entitlements, fakesign them with ldid and the entitlements you want (`ldid -S<path/to/entitlements.plist> <path/to/binary>`) and TrollStore will preserve the entitlements when resigning them with the fake root certificate on installation. This gives you a lot of possibilities, some of which are explained below.\n\n### Banned entitlements\n\niOS 15 on A12+ has banned the following three entitlements related to running unsigned code, these are impossible to get without a PPL bypass, apps signed with them will crash on launch.\n\n`com.apple.private.cs.debugger`\n\n`dynamic-codesigning`\n\n`com.apple.private.skip-library-validation`\n\n### Unsandboxing\n\nYour app can run unsandboxed using one of the following entitlements:\n\n```xml\n<key>com.apple.private.security.container-required</key>\n<false/>\n```\n\n```xml\n<key>com.apple.private.security.no-container</key>\n<true/>\n```\n\n```xml\n<key>com.apple.private.security.no-sandbox</key>\n<true/>\n```\n\nThe third one is recommended if you still want a sandbox container for your application.\n\nYou might also need the platform-application entitlement in order for these to work properly:\n\n```xml\n<key>platform-application</key>\n<true/>\n```\n\nPlease note that the platform-application entitlement causes side effects such as some parts of the sandbox becoming tighter, so you may need additional private entitlements to circumvent that. (For example afterwards you need an exception entitlement for every single IOKit user client class you want to access).\n\nIn order for an app with `com.apple.private.security.no-sandbox` and `platform-application` to be able to access it's own data container, you might need the additional entitlement:\n\n```xml\n<key>com.apple.private.security.storage.AppDataContainers</key>\n<true/>\n```\n\n### Root Helpers\n\nWhen your app is not sandboxed, you can spawn other binaries using posix_spawn, you can also spawn binaries as root with the following entitlement:\n\n```xml\n<key>com.apple.private.persona-mgmt</key>\n<true/>\n```\n\nYou can also add your own binaries into your app bundle.\n\nAfterwards you can use the [spawnRoot function in TSUtil.m](./Shared/TSUtil.m#L79) to spawn the binary as root.\n\n### Things that are not possible using TrollStore\n\n- Getting proper platformization (`TF_PLATFORM` / `CS_PLATFORMIZED`)\n- Spawning a launch daemon (Would need `CS_PLATFORMIZED`)\n- Injecting a tweak into a system process (Would need `TF_PLATFORM`, a userland PAC bypass and a PMAP trust level bypass)\n\n### Compilation\n\nTo compile TrollStore, ensure [theos](https://theos.dev/docs/installation) is installed. Additionaly ensure [brew](https://brew.sh/) is installed and install [libarchive](https://formulae.brew.sh/formula/libarchive) from brew.\n\n## Credits and Further Reading\n\n[@alfiecg_dev](https://twitter.com/alfiecg_dev/) - Found the CoreTrust bug that allows TrollStore to work through patchdiffing and worked on automating the bypass.\n\nGoogle Threat Analysis Group - Found the CoreTrust bug as part of an in-the-wild spyware chain and reported it to Apple.\n\n[@LinusHenze](https://twitter.com/LinusHenze) - Found the installd bypass used to install TrollStore on iOS 14-15.6.1 via TrollHelperOTA, as well as the original CoreTrust bug used in TrollStore 1.0.\n\n[Fugu15 Presentation](https://youtu.be/rPTifU1lG7Q)\n\n[Write-Up on the first CoreTrust bug with more information](https://worthdoingbadly.com/coretrust/).\n"
  },
  {
    "path": "RootHelper/Makefile",
    "content": "TARGET := iphone:clang:16.5:14.0\nARCHS = arm64\n\nifdef TROLLSTORE_LITE\nHELPER_NAME = trollstorehelper_lite\nelse\nHELPER_NAME = trollstorehelper\nTARGET_CODESIGN = ../Exploits/fastPathSign/fastPathSign\nendif\n\ninclude $(THEOS)/makefiles/common.mk\n\nTOOL_NAME = $(HELPER_NAME)\n\n$(HELPER_NAME)_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m) $(wildcard ../ChOma/src/*.c)\n\nifndef TROLLSTORE_LITE\n$(HELPER_NAME)_FILES += ../Exploits/fastPathSign/src/coretrust_bug.c ../Exploits/fastPathSign/src/codesign.m\n$(HELPER_NAME)_CODESIGN_FLAGS = --entitlements entitlements.plist\n$(HELPER_NAME)_LDFLAGS = -L../ChOma/external/ios -lcrypto\nelse\n$(HELPER_NAME)_CODESIGN_FLAGS = -Sentitlements.plist\nendif\n \n$(HELPER_NAME)_CFLAGS = -fobjc-arc -I../Shared $(shell pkg-config --cflags libcrypto) -I../ChOma/src -I../Exploits/fastPathSign/src -I$(shell brew --prefix)/opt/libarchive/include\n\n$(HELPER_NAME)_INSTALL_PATH = /usr/local/bin\n$(HELPER_NAME)_LIBRARIES = archive\n$(HELPER_NAME)_FRAMEWORKS = CoreTelephony\n$(HELPER_NAME)_PRIVATE_FRAMEWORKS = SpringBoardServices BackBoardServices MobileContainerManager FrontBoardServices RunningBoardServices\n\nifdef TROLLSTORE_LITE\n$(HELPER_NAME)_CFLAGS += -DTROLLSTORE_LITE -DDISABLE_SIGNING=1\nendif\n\ninclude $(THEOS_MAKE_PATH)/tool.mk\n"
  },
  {
    "path": "RootHelper/control",
    "content": "Package: com.opa334.trollstoreroothelper\nName: trollstoreroothelper\nVersion: 2.1\nArchitecture: iphoneos-arm\nDescription: An awesome tool of some sort!!\nMaintainer: opa334\nAuthor: opa334\nSection: System\nTag: role::hacker\n"
  },
  {
    "path": "RootHelper/devmode.h",
    "content": "#import <Foundation/Foundation.h>\n\nBOOL checkDeveloperMode(void);\nBOOL armDeveloperMode(BOOL* alreadyEnabled);"
  },
  {
    "path": "RootHelper/devmode.m",
    "content": "@import Foundation;\n\n#ifndef __XPC_H__\n// Types\ntypedef NSObject* xpc_object_t;\ntypedef xpc_object_t xpc_connection_t;\ntypedef void (^xpc_handler_t)(xpc_object_t object);\n\n// Communication\nextern xpc_connection_t xpc_connection_create_mach_service(const char* name, dispatch_queue_t targetq, uint64_t flags);\nextern void xpc_connection_set_event_handler(xpc_connection_t connection, xpc_handler_t handler);\nextern void xpc_connection_resume(xpc_connection_t connection);\nextern void xpc_connection_send_message_with_reply(xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq, xpc_handler_t handler);\nextern xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t connection, xpc_object_t message);\nextern xpc_object_t xpc_dictionary_get_value(xpc_object_t xdict, const char *key);\n#endif\n\n// Serialization\nextern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xpcattrs);\nextern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef attrs);\nextern xpc_object_t _CFXPCCreateXPCMessageWithCFObject(CFTypeRef obj);\nextern CFTypeRef _CFXPCCreateCFObjectFromXPCMessage(xpc_object_t obj);\n\n\ntypedef enum {\n    kAMFIActionArm = 0,     // Trigger a prompt asking the user to enable developer mode on the next reboot\n                            // (regardless of current state)\n    kAMFIActionDisable = 1, // Disable developer mode if it's currently enabled. Takes effect immediately.\n    kAMFIActionStatus = 2,  // Returns a dict: {success: bool, status: bool, armed: bool}\n} AMFIXPCAction;\n\nxpc_connection_t startConnection(void) {\n\txpc_connection_t connection = xpc_connection_create_mach_service(\"com.apple.amfi.xpc\", NULL, 0);\n    if (!connection) {\n        NSLog(@\"[startXPCConnection] Failed to create XPC connection to amfid\");\n        return nil;\n    }\n    xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {\n    });\n    xpc_connection_resume(connection);\n    return connection;\n}\n\nNSDictionary* sendXPCRequest(xpc_connection_t connection, AMFIXPCAction action) {\n    xpc_object_t message = _CFXPCCreateXPCMessageWithCFObject((__bridge CFDictionaryRef) @{@\"action\": @(action)});\n    xpc_object_t replyMsg = xpc_connection_send_message_with_reply_sync(connection, message);\n    if (!replyMsg) {\n        NSLog(@\"[sendXPCRequest] got no reply from amfid\");\n        return nil;\n    }\n\n    xpc_object_t replyObj = xpc_dictionary_get_value(replyMsg, \"cfreply\");\n    if (!replyObj) {\n        NSLog(@\"[sendXPCRequest] got reply but no cfreply\");\n        return nil;\n    }\n\n    NSDictionary* asCF = (__bridge NSDictionary*)_CFXPCCreateCFObjectFromXPCMessage(replyObj);\n    return asCF;\n}\n\nBOOL getDeveloperModeState(xpc_connection_t connection) {\n    NSDictionary* reply = sendXPCRequest(connection, kAMFIActionStatus);\n    if (!reply) {\n        NSLog(@\"[getDeveloperModeState] failed to get reply\");\n        return NO;\n    }\n\n    NSLog(@\"[getDeveloperModeState] got reply %@\", reply);\n\n    NSObject* success = reply[@\"success\"];\n    if (!success || ![success isKindOfClass:[NSNumber class]] || ![(NSNumber*)success boolValue]) {\n        NSLog(@\"[getDeveloperModeState] request failed with error %@\", reply[@\"error\"]);\n        return NO;\n    }\n\n    NSObject* status = reply[@\"status\"];\n    if (!status || ![status isKindOfClass:[NSNumber class]]) {\n        NSLog(@\"[getDeveloperModeState] request succeeded but no status\");\n        return NO;\n    }\n\n    return [(NSNumber*)status boolValue];\n}\n\nBOOL setDeveloperModeState(xpc_connection_t connection, BOOL enable) {\n    NSDictionary* reply = sendXPCRequest(connection, enable ? kAMFIActionArm : kAMFIActionDisable);\n    if (!reply) {\n        NSLog(@\"[setDeveloperModeState] failed to get reply\");\n        return NO;\n    }\n\n    NSObject* success = reply[@\"success\"];\n    if (!success || ![success isKindOfClass:[NSNumber class]] || ![(NSNumber*)success boolValue]) {\n        NSLog(@\"[setDeveloperModeState] request failed with error %@\", reply[@\"error\"]);\n        return NO;\n    }\n\n    return YES;\n}\n\nBOOL checkDeveloperMode(void) {\n    // Developer mode does not exist before iOS 16\n    if (@available(iOS 16, *)) {\n        xpc_connection_t connection = startConnection();\n        if (!connection) {\n            NSLog(@\"[checkDeveloperMode] failed to start connection\");\n            // Assume it's disabled\n            return NO;\n        }\n\n        return getDeveloperModeState(connection);\n    } else {\n        return YES;\n    }\n}\n\nBOOL armDeveloperMode(BOOL* alreadyEnabled) {\n    // Developer mode does not exist before iOS 16\n    if (@available(iOS 16, *)) {\n        xpc_connection_t connection = startConnection();\n        if (!connection) {\n            NSLog(@\"[armDeveloperMode] failed to start connection\");\n            return NO;\n        }\n\n        BOOL enabled = getDeveloperModeState(connection);\n        if (alreadyEnabled) {\n            *alreadyEnabled = enabled;\n        }\n\n        if (enabled) {\n            // NSLog(@\"[armDeveloperMode] already enabled\");\n            return YES;\n        }\n\n        BOOL success = setDeveloperModeState(connection, YES);\n        if (!success) {\n            NSLog(@\"[armDeveloperMode] failed to arm\");\n            return NO;\n        }\n    }\n\n    return YES;\n}\n"
  },
  {
    "path": "RootHelper/entitlements.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>platform-application</key>\n\t<true/>\n\t<key>com.apple.private.security.container-required</key>\n\t<false/>\n\t<key>com.apple.private.security.no-sandbox</key>\n\t<true/>\n\t<key>com.apple.private.security.container-manager</key>\n\t<true/>\n\t<key>com.apple.private.MobileContainerManager.allowed</key>\n\t<true/>\n\t<key>com.apple.private.coreservices.canmaplsdatabase</key>\n\t<true/>\n\t<key>com.apple.lsapplicationworkspace.rebuildappdatabases</key>\n\t<true/>\n\t<key>com.apple.private.security.storage.AppBundles</key>\n\t<true/>\n\t<key>com.apple.private.security.storage.MobileDocuments</key>\n\t<true/>\n\t<key>com.apple.private.security.storage-exempt.heritable</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.allowed</key>\n\t<true/>\n\t<key>com.apple.private.uninstall.deletion</key>\n\t<true/>\n\t<key>com.apple.springboard.launchapplications</key>\n\t<true/>\n\t<key>com.apple.backboardd.launchapplications</key>\n\t<true/>\n\t<key>com.apple.frontboard.launchapplications</key>\n\t<true/>\n\t<key>com.apple.multitasking.termination</key>\n\t<true/>\n\t<key>com.apple.private.mobileinstall.allowedSPI</key>\n\t<array>\n\t\t<string>InstallForLaunchServices</string>\n\t\t<string>Install</string>\n\t\t<string>UninstallForLaunchServices</string>\n\t\t<string>Uninstall</string>\n\t\t<string>UpdatePlaceholderMetadata</string>\n\t</array>\n\t<key>com.apple.private.amfi.developer-mode-control</key>\n\t<true/>\n\t<key>com.apple.frontboard.shutdown</key>\n\t<true/>\n\t<key>com.apple.runningboard.process-state</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "RootHelper/jit.h",
    "content": "#import <Foundation/Foundation.h>\n\nint enableJIT(NSString *bundleID);\n"
  },
  {
    "path": "RootHelper/jit.m",
    "content": "@import Foundation;\n@import Darwin;\n\n@interface RBSProcessPredicate\n+ (instancetype)predicateMatchingBundleIdentifier:(NSString *)bundleID;\n@end\n\n@interface RBSProcessHandle\n+ (instancetype)handleForPredicate:(RBSProcessPredicate *)predicate error:(NSError **)error;\n- (int)rbs_pid;\n@end\n\n#define PT_DETACH       11\n#define PT_ATTACHEXC    14\nint\t ptrace(int request, pid_t pid, caddr_t addr, int data);\n\nint enableJIT(NSString *bundleID) {\n#ifdef EMBEDDED_ROOT_HELPER\n \treturn -1;\n#else\n \tRBSProcessPredicate *predicate = [RBSProcessPredicate predicateMatchingBundleIdentifier:bundleID];\n\tRBSProcessHandle* process = [RBSProcessHandle handleForPredicate:predicate error:nil];\n\tint pid = process.rbs_pid;\n\n\tif (!pid)\n\t{\n\t\treturn ESRCH;\n\t}\n\n\tint ret = ptrace(PT_ATTACHEXC, pid, 0, 0);\n\tif (ret == -1)\n\t{\n\t\treturn errno;\n\t}\n\n\tusleep(100000);\n\tret = ptrace(PT_DETACH, pid, 0, 0);\n\tif (ret == -1)\n\t{\n\t\treturn errno;\n\t}\n\treturn 0;\n#endif\n}\n\n"
  },
  {
    "path": "RootHelper/main.m",
    "content": "#import <stdio.h>\n#import \"unarchive.h\"\n@import Foundation;\n#import \"uicache.h\"\n#import <sys/stat.h>\n#import <dlfcn.h>\n#import <spawn.h>\n#import <objc/runtime.h>\n#import <TSUtil.h>\n#import <sys/utsname.h>\n#import <mach-o/loader.h>\n#import <mach-o/fat.h>\n#import \"devmode.h\"\n#import \"jit.h\"\n#ifndef EMBEDDED_ROOT_HELPER\n#import \"codesign.h\"\n#import \"coretrust_bug.h\"\n#import \"FAT.h\"\n#import \"MachO.h\"\n#import \"FileStream.h\"\n#import \"Host.h\"\n#endif\n\n#import <SpringBoardServices/SpringBoardServices.h>\n#import <FrontBoardServices/FBSSystemService.h>\n#import <Security/Security.h>\n#import <libroot.h>\n\n#ifdef EMBEDDED_ROOT_HELPER\n#define MAIN_NAME rootHelperMain\n#else\n#define MAIN_NAME main\n#endif\n\nvoid cleanRestrictions(void);\n\nextern mach_msg_return_t SBReloadIconForIdentifier(mach_port_t machport, const char* identifier);\n@interface SBSHomeScreenService : NSObject\n- (void)reloadIcons;\n@end\nextern NSString* BKSActivateForEventOptionTypeBackgroundContentFetching;\nextern NSString* BKSOpenApplicationOptionKeyActivateForEvent;\n\nextern void BKSTerminateApplicationForReasonAndReportWithDescription(NSString *bundleID, int reasonID, bool report, NSString *description);\n\n#define kCFPreferencesNoContainer CFSTR(\"kCFPreferencesNoContainer\")\n\ntypedef CFPropertyListRef (*_CFPreferencesCopyValueWithContainerType)(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath);\ntypedef void (*_CFPreferencesSetValueWithContainerType)(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath);\ntypedef Boolean (*_CFPreferencesSynchronizeWithContainerType)(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath);\ntypedef CFArrayRef (*_CFPreferencesCopyKeyListWithContainerType)(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath);\ntypedef CFDictionaryRef (*_CFPreferencesCopyMultipleWithContainerType)(CFArrayRef keysToFetch, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName, CFStringRef containerPath);\n\nBOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePersistenceHelper, NSString* sourceRootHelper);\n\nNSArray<LSApplicationProxy*>* applicationsWithGroupId(NSString* groupId)\n{\n\tLSEnumerator* enumerator = [LSEnumerator enumeratorForApplicationProxiesWithOptions:0];\n\tenumerator.predicate = [NSPredicate predicateWithFormat:@\"groupContainerURLs[%@] != nil\", groupId];\n\treturn enumerator.allObjects;\n}\n\nNSSet<NSString*>* systemURLSchemes(void)\n{\n\tLSEnumerator* enumerator = [LSEnumerator enumeratorForApplicationProxiesWithOptions:0];\n\n\tNSMutableSet* systemURLSchemesSet = [NSMutableSet new];\n\tLSApplicationProxy* proxy;\n\twhile(proxy = [enumerator nextObject])\n\t{\n\t\tif(isRemovableSystemApp(proxy.bundleIdentifier) || ![proxy.bundleURL.path hasPrefix:@\"/private/var/containers\"])\n\t\t{\n\t\t\tfor(NSString* claimedURLScheme in proxy.claimedURLSchemes)\n\t\t\t{\n\t\t\t\tif([claimedURLScheme isKindOfClass:NSString.class])\n\t\t\t\t{\n\t\t\t\t\t[systemURLSchemesSet addObject:claimedURLScheme.lowercaseString];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn systemURLSchemesSet.copy;\n}\n\nNSSet<NSString*>* immutableAppBundleIdentifiers(void)\n{\n\tNSMutableSet* systemAppIdentifiers = [NSMutableSet new];\n\n\tLSEnumerator* enumerator = [LSEnumerator enumeratorForApplicationProxiesWithOptions:0];\n\tLSApplicationProxy* appProxy;\n\twhile(appProxy = [enumerator nextObject])\n\t{\n\t\tif(appProxy.installed)\n\t\t{\n\t\t\tif(![appProxy.bundleURL.path hasPrefix:@\"/private/var/containers\"])\n\t\t\t{\n\t\t\t\t[systemAppIdentifiers addObject:appProxy.bundleIdentifier.lowercaseString];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn systemAppIdentifiers.copy;\n}\n\nNSDictionary* infoDictionaryForAppPath(NSString* appPath)\n{\n\tif(!appPath) return nil;\n\tNSString* infoPlistPath = [appPath stringByAppendingPathComponent:@\"Info.plist\"];\n\treturn [NSDictionary dictionaryWithContentsOfFile:infoPlistPath];\n}\n\nNSString* appIdForAppPath(NSString* appPath)\n{\n\tif(!appPath) return nil;\n\treturn infoDictionaryForAppPath(appPath)[@\"CFBundleIdentifier\"];\n}\n\nNSString* appMainExecutablePathForAppPath(NSString* appPath)\n{\n\tif(!appPath) return nil;\n\treturn [appPath stringByAppendingPathComponent:infoDictionaryForAppPath(appPath)[@\"CFBundleExecutable\"]];\n}\n\nNSString* appPathForAppId(NSString* appId)\n{\n\tif(!appId) return nil;\n\tfor(NSString* appPath in trollStoreInstalledAppBundlePaths())\n\t{\n\t\tif([appIdForAppPath(appPath) isEqualToString:appId])\n\t\t{\n\t\t\treturn appPath;\n\t\t}\n\t}\n\treturn nil;\n}\n\nNSString* findAppNameInBundlePath(NSString* bundlePath)\n{\n\tNSArray* bundleItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePath error:nil];\n\tfor(NSString* bundleItem in bundleItems)\n\t{\n\t\tif([bundleItem.pathExtension isEqualToString:@\"app\"])\n\t\t{\n\t\t\treturn bundleItem;\n\t\t}\n\t}\n\treturn nil;\n}\n\nNSString* findAppPathInBundlePath(NSString* bundlePath)\n{\n\tNSString* appName = findAppNameInBundlePath(bundlePath);\n\tif(!appName) return nil;\n\treturn [bundlePath stringByAppendingPathComponent:appName];\n}\n\nNSURL* findAppURLInBundleURL(NSURL* bundleURL)\n{\n\tNSString* appName = findAppNameInBundlePath(bundleURL.path);\n\tif(!appName) return nil;\n\treturn [bundleURL URLByAppendingPathComponent:appName];\n}\n\nBOOL isMachoFile(NSString* filePath)\n{\n\tFILE* file = fopen(filePath.fileSystemRepresentation, \"r\");\n\tif(!file) return NO;\n\n\tfseek(file, 0, SEEK_SET);\n\tuint32_t magic;\n\tfread(&magic, sizeof(uint32_t), 1, file);\n\tfclose(file);\n\n\treturn magic == FAT_MAGIC || magic == FAT_CIGAM || magic == MH_MAGIC_64 || magic == MH_CIGAM_64;\n}\n\nvoid fixPermissionsOfAppBundle(NSString* appBundlePath)\n{\n\t// Apply correct permissions (First run, set everything to 644, owner 33)\n\tNSURL* fileURL;\n\tNSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appBundlePath] includingPropertiesForKeys:nil options:0 errorHandler:nil];\n\twhile(fileURL = [enumerator nextObject])\n\t{\n\t\tNSString* filePath = fileURL.path;\n\t\tchown(filePath.fileSystemRepresentation, 33, 33);\n\t\tchmod(filePath.fileSystemRepresentation, 0644);\n\t}\n\n\t// Apply correct permissions (Second run, set executables and directories to 0755)\n\tenumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appBundlePath] includingPropertiesForKeys:nil options:0 errorHandler:nil];\n\twhile(fileURL = [enumerator nextObject])\n\t{\n\t\tNSString* filePath = fileURL.path;\n\n\t\tBOOL isDir;\n\t\t[[NSFileManager defaultManager] fileExistsAtPath:fileURL.path isDirectory:&isDir];\n\n\t\tif(isDir || isMachoFile(filePath))\n\t\t{\n\t\t\tchmod(filePath.fileSystemRepresentation, 0755);\n\t\t}\n\t}\n}\n\nNSArray* TSURLScheme(void)\n{\n\treturn @[\n\t\t@{\n\t\t\t@\"CFBundleURLName\" : @\"com.apple.Magnifier\",\n\t\t\t@\"CFBundleURLSchemes\" : @[\n\t\t\t\t@\"apple-magnifier\"\n\t\t\t]\n\t\t}\n\t];\n}\n\nBOOL getTSURLSchemeState(NSString* customAppPath)\n{\n\tNSString* pathToUse = customAppPath ?: trollStoreAppPath();\n\n\tNSDictionary* trollStoreInfoDict = infoDictionaryForAppPath(pathToUse);\n\treturn (BOOL)trollStoreInfoDict[@\"CFBundleURLTypes\"];\n}\n\nvoid setTSURLSchemeState(BOOL newState, NSString* customAppPath)\n{\n\tNSString* tsAppPath = trollStoreAppPath();\n\tNSString* pathToUse = customAppPath ?: tsAppPath;\n\tif(newState != getTSURLSchemeState(pathToUse))\n\t{\n\t\tNSDictionary* trollStoreInfoDict = infoDictionaryForAppPath(pathToUse);\n\t\tNSMutableDictionary* trollStoreInfoDictM = trollStoreInfoDict.mutableCopy;\n\t\tif(newState)\n\t\t{\n\t\t\ttrollStoreInfoDictM[@\"CFBundleURLTypes\"] = TSURLScheme();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t[trollStoreInfoDictM removeObjectForKey:@\"CFBundleURLTypes\"];\n\t\t}\n\t\tNSString* outPath = [pathToUse stringByAppendingPathComponent:@\"Info.plist\"];\n\t\t[trollStoreInfoDictM.copy writeToURL:[NSURL fileURLWithPath:outPath] error:nil];\n\t}\n}\n\n#ifdef TROLLSTORE_LITE\n\nBOOL isLdidInstalled(void)\n{\n\t// Since TrollStore Lite depends on ldid, we assume it exists\n\treturn YES;\n}\n\nNSString *getLdidPath(void)\n{\n\treturn JBROOT_PATH(@\"/usr/bin/ldid\");\n}\n\n#else\n\nvoid installLdid(NSString* ldidToCopyPath, NSString* ldidVersion)\n{\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:ldidToCopyPath]) return;\n\n\tNSString* ldidPath = [trollStoreAppPath() stringByAppendingPathComponent:@\"ldid\"];\n\tNSString* ldidVersionPath = [trollStoreAppPath() stringByAppendingPathComponent:@\"ldid.version\"];\n\n\tif([[NSFileManager defaultManager] fileExistsAtPath:ldidPath])\n\t{\n\t\t[[NSFileManager defaultManager] removeItemAtPath:ldidPath error:nil];\n\t}\n\n\t[[NSFileManager defaultManager] copyItemAtPath:ldidToCopyPath toPath:ldidPath error:nil];\n\n\tNSData* ldidVersionData = [ldidVersion dataUsingEncoding:NSUTF8StringEncoding];\n\t[ldidVersionData writeToFile:ldidVersionPath atomically:YES];\n\n\tchmod(ldidPath.fileSystemRepresentation, 0755);\n\tchmod(ldidVersionPath.fileSystemRepresentation, 0644);\n}\n\nBOOL isLdidInstalled(void)\n{\n\tNSString* ldidPath = [trollStoreAppPath() stringByAppendingPathComponent:@\"ldid\"];\n\treturn [[NSFileManager defaultManager] fileExistsAtPath:ldidPath];\n}\n\nNSString *getLdidPath(void)\n{\n\treturn [trollStoreAppPath() stringByAppendingPathComponent:@\"ldid\"];\n}\n\n#endif\n\nint runLdid(NSArray* args, NSString** output, NSString** errorOutput)\n{\n\tNSString* ldidPath = getLdidPath();\n\tNSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new];\n\t[argsM insertObject:ldidPath.lastPathComponent atIndex:0];\n\n\tNSUInteger argCount = [argsM count];\n\tchar **argsC = (char **)malloc((argCount + 1) * sizeof(char*));\n\n\tfor (NSUInteger i = 0; i < argCount; i++)\n\t{\n\t\targsC[i] = strdup([[argsM objectAtIndex:i] UTF8String]);\n\t}\n\targsC[argCount] = NULL;\n\n\tposix_spawn_file_actions_t action;\n\tposix_spawn_file_actions_init(&action);\n\n\tint outErr[2];\n\tpipe(outErr);\n\tposix_spawn_file_actions_adddup2(&action, outErr[1], STDERR_FILENO);\n\tposix_spawn_file_actions_addclose(&action, outErr[0]);\n\n\tint out[2];\n\tpipe(out);\n\tposix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);\n\tposix_spawn_file_actions_addclose(&action, out[0]);\n\t\n\tpid_t task_pid;\n\tint status = -200;\n\tNSLog(@\"About to spawn ldid (%@) with args %@\", ldidPath, args);\n\tint spawnError = posix_spawn(&task_pid, [ldidPath fileSystemRepresentation], &action, NULL, (char* const*)argsC, NULL);\n\tfor (NSUInteger i = 0; i < argCount; i++)\n\t{\n\t\tfree(argsC[i]);\n\t}\n\tfree(argsC);\n\n\tif(spawnError != 0)\n\t{\n\t\tNSLog(@\"ldid failed to spawn with error %d (%s)\\n\", spawnError, strerror(spawnError));\n\t\treturn spawnError;\n\t}\n\n\tdo\n\t{\n\t\tif (waitpid(task_pid, &status, 0) != -1) {\n\t\t\t//printf(\"Child status %dn\", WEXITSTATUS(status));\n\t\t} else\n\t\t{\n\t\t\tperror(\"waitpid\");\n\t\t\treturn -222;\n\t\t}\n\t} while (!WIFEXITED(status) && !WIFSIGNALED(status));\n\n\tclose(outErr[1]);\n\tclose(out[1]);\n\n\tNSString* ldidOutput = getNSStringFromFile(out[0]);\n\tif(output)\n\t{\n\t\t*output = ldidOutput;\n\t}\n\n\tNSString* ldidErrorOutput = getNSStringFromFile(outErr[0]);\n\tif(errorOutput)\n\t{\n\t\t*errorOutput = ldidErrorOutput;\n\t}\n\n\treturn WEXITSTATUS(status);\n}\n\nBOOL certificateHasDataForExtensionOID(SecCertificateRef certificate, CFStringRef oidString)\n{\n\tif(certificate == NULL || oidString == NULL)\n\t{\n\t\tNSLog(@\"[certificateHasDataForExtensionOID] attempted to check null certificate or OID\");\n\t\treturn NO;\n\t}\n\t\n\tCFDataRef extensionData = SecCertificateCopyExtensionValue(certificate, oidString, NULL);\n\tif(extensionData != NULL)\n\t{\n\t\tCFRelease(extensionData);\n\t\treturn YES;\n\t}\n\t\n\treturn NO;\n}\n\nBOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef)\n{\n\tif(codeRef == NULL)\n\t{\n\t\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] attempted to check cert chain of null static code object\");\n\t\treturn NO;\n\t}\n\t\n\tCFDictionaryRef signingInfo = NULL;\n\tOSStatus result;\n  \n\tresult = SecCodeCopySigningInformation(codeRef, kSecCSSigningInformation, &signingInfo);\n\n\tif(result != errSecSuccess)\n\t{\n\t\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] failed to copy signing info from static code\");\n\t\treturn NO;\n\t}\n\t\n\tCFArrayRef certificates = CFDictionaryGetValue(signingInfo, kSecCodeInfoCertificates);\n\tif(certificates == NULL || CFArrayGetCount(certificates) == 0)\n\t{\n\t\treturn NO;\n\t}\n\n\t// If we match the standard Apple policy, we are signed properly, but we haven't been deliberately signed with a custom root\n\t\n\tSecPolicyRef appleAppStorePolicy = SecPolicyCreateWithProperties(kSecPolicyAppleiPhoneApplicationSigning, NULL);\n\n\tSecTrustRef trust = NULL;\n\tSecTrustCreateWithCertificates(certificates, appleAppStorePolicy, &trust);\n\n\tif(SecTrustEvaluateWithError(trust, nil))\n\t{\n\t\tCFRelease(trust);\n\t\tCFRelease(appleAppStorePolicy);\n\t\tCFRelease(signingInfo);\n\t\t\n\t\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] found certificate extension, but was issued by Apple (App Store)\");\n\t\treturn NO;\n\t}\n\n\t// We haven't matched Apple, so keep going. Is the app profile signed?\n\t\t\n\tCFRelease(appleAppStorePolicy);\n\t\n\tSecPolicyRef appleProfileSignedPolicy = SecPolicyCreateWithProperties(kSecPolicyAppleiPhoneProfileApplicationSigning, NULL);\n\tif(SecTrustSetPolicies(trust, appleProfileSignedPolicy) != errSecSuccess)\n\t{\n\t\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] error replacing trust policy to check for profile-signed app\");\n\t\tCFRelease(trust);\n\t\tCFRelease(signingInfo);\n\t\treturn NO;\n\t}\n\t\t\n\tif(SecTrustEvaluateWithError(trust, nil))\n\t{\n\t\tCFRelease(trust);\n\t\tCFRelease(appleProfileSignedPolicy);\n\t\tCFRelease(signingInfo);\n\t\t\n\t\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] found certificate extension, but was issued by Apple (profile-signed)\");\n\t\treturn NO;\n\t}\n\t\n\t// Still haven't matched Apple. Are we using a custom root that would take the App Store fastpath?\n\tCFRelease(appleProfileSignedPolicy);\n\t\n\t// Cert chain should be of length 3\n\tif(CFArrayGetCount(certificates) != 3)\n\t{\n\t\tCFRelease(signingInfo);\n\t\t\n\t\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] certificate chain length != 3\");\n\t\treturn NO;\n\t}\n\t\t\n\t// AppleCodeSigning only checks for the codeSigning EKU by default\n\tSecPolicyRef customRootPolicy = SecPolicyCreateWithProperties(kSecPolicyAppleCodeSigning, NULL);\n\tSecPolicySetOptionsValue(customRootPolicy, CFSTR(\"LeafMarkerOid\"), CFSTR(\"1.2.840.113635.100.6.1.3\"));\n\t\n\tif(SecTrustSetPolicies(trust, customRootPolicy) != errSecSuccess)\n\t{\n\t\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] error replacing trust policy to check for custom root\");\n\t\tCFRelease(trust);\n\t\tCFRelease(signingInfo);\n\t\treturn NO;\n\t}\n\n\t// Need to add our certificate chain to the anchor as it is expected to be a self-signed root\n\tSecTrustSetAnchorCertificates(trust, certificates);\n\t\n\tBOOL evaluatesToCustomAnchor = SecTrustEvaluateWithError(trust, nil);\n\tNSLog(@\"[codeCertChainContainsFakeAppStoreExtensions] app signed with non-Apple certificate %@ using valid custom certificates\", evaluatesToCustomAnchor ? @\"IS\" : @\"is NOT\");\n\t\n\tCFRelease(trust);\n\tCFRelease(customRootPolicy);\n\tCFRelease(signingInfo);\n\t\n\treturn evaluatesToCustomAnchor;\n}\n\nBOOL isSameFile(NSString *path1, NSString *path2)\n{\n\tstruct stat sb1;\n\tstruct stat sb2;\n\tstat(path1.fileSystemRepresentation, &sb1);\n\tstat(path2.fileSystemRepresentation, &sb2);\n\treturn sb1.st_ino == sb2.st_ino;\n}\n\n#ifdef EMBEDDED_ROOT_HELPER\n// The embedded root helper is not able to sign apps\n// But it does not need that functionality anyways\nint signApp(NSString* appPath)\n{\n\treturn -1;\n}\n#else\nint signAdhoc(NSString *filePath, NSDictionary *entitlements)\n{\n\t//if (@available(iOS 16, *)) {\n\t//\treturn codesign_sign_adhoc(filePath.fileSystemRepresentation, true, entitlements);\n\t//}\n\t// If iOS 14 is so great, how come there is no iOS 14 2?????\n\t//else {\n\t\tif(!isLdidInstalled()) return 173;\n\n\t\tNSString *entitlementsPath = nil;\n\t\tNSString *signArg = @\"-s\";\n\t\tNSString* errorOutput;\n\t\tif(entitlements)\n\t\t{\n\t\t\tNSData *entitlementsXML = [NSPropertyListSerialization dataWithPropertyList:entitlements format:NSPropertyListXMLFormat_v1_0 options:0 error:nil];\n\t\t\tif (entitlementsXML) {\n\t\t\t\tentitlementsPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString] stringByAppendingPathExtension:@\"plist\"];\n\t\t\t\t[entitlementsXML writeToFile:entitlementsPath atomically:NO];\n\t\t\t\tsignArg = [@\"-S\" stringByAppendingString:entitlementsPath];\n\t\t\t}\n\t\t\t\n\t\t}\n\t\tint ldidRet = runLdid(@[signArg, filePath], nil, &errorOutput);\n\t\tif (entitlementsPath) {\n\t\t\t[[NSFileManager defaultManager] removeItemAtPath:entitlementsPath error:nil];\n\t\t}\n\n\t\tNSLog(@\"ldid exited with status %d\", ldidRet);\n\n\t\tNSLog(@\"- ldid error output start -\");\n\n\t\tprintMultilineNSString(errorOutput);\n\n\t\tNSLog(@\"- ldid error output end -\");\n\n\t\tif(ldidRet == 0)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn 175;\n\t\t}\n\t//}\n}\n\nint signApp(NSString* appPath)\n{\n\tNSDictionary* appInfoDict = infoDictionaryForAppPath(appPath);\n\tif(!appInfoDict) return 172;\n\n\tNSString* mainExecutablePath = appMainExecutablePathForAppPath(appPath);\n\tif(!mainExecutablePath) return 176;\n\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:mainExecutablePath]) return 174;\n\n#ifndef TROLLSTORE_LITE\n\t// Check if the bundle has had a supported exploit pre-applied\n\tEXPLOIT_TYPE declaredPreAppliedExploitType = getDeclaredExploitTypeFromInfoDictionary(appInfoDict);\n\tif(isPlatformVulnerableToExploitType(declaredPreAppliedExploitType))\n\t{\n\t\tNSLog(@\"[signApp] taking fast path for app which declares use of a supported pre-applied exploit (%@)\", mainExecutablePath);\n\t\treturn 0;\n\t}\n\telse if (declaredPreAppliedExploitType != 0)\n\t{\n\t\tNSLog(@\"[signApp] app (%@) declares use of a pre-applied exploit that is not supported on this device. Proceeding to re-sign...\", mainExecutablePath);\n\t}\n\n\t// If the app doesn't declare a pre-applied exploit, and the host supports fake custom root certs,\n\t// we can also skip doing any work here when that app is signed with fake roots\n\t// If not, with the new bypass, a previously modified binary should failed to be adhoc signed, and\n\t// reapplying the bypass should produce an identical binary\n\tif(isPlatformVulnerableToExploitType(EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1))\n\t{\n\t\tSecStaticCodeRef codeRef = getStaticCodeRef(mainExecutablePath);\n\t\tif(codeRef != NULL)\n\t\t{\n\t\t\tif(codeCertChainContainsFakeAppStoreExtensions(codeRef))\n\t\t\t{\n\t\t\t\tNSLog(@\"[signApp] taking fast path for app signed using a custom root certificate (%@)\", mainExecutablePath);\n\t\t\t\tCFRelease(codeRef);\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tCFRelease(codeRef);\n\t\t}\n\t}\n\n\t// On iOS 16+, binaries with certain entitlements requires developer mode to be enabled, so we'll check\n\t// while we're fixing entitlements\n\tBOOL requiresDevMode = NO;\n#endif\n\n\t// The majority of IPA decryption utilities only decrypt the main executable of the app bundle\n\t// As a result, we cannot bail on the entire app if an additional binary is encrypted (e.g. app extensions)\n\t// Instead, we will display a warning to the user, and warn them that the app may not work properly\n\tBOOL hasAdditionalEncryptedBinaries = NO;\n\n\tNSURL* fileURL;\n\tNSDirectoryEnumerator *enumerator;\n\n\t// Due to how the new CT bug works, in order for data containers to work properly we need to add the\n\t// com.apple.private.security.container-required=<bundle-identifier> entitlement to every binary inside a bundle\n\t// For this we will want to first collect info about all the bundles in the app by seeking for Info.plist files and adding the ent to the main binary\n\tenumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil];\n\twhile(fileURL = [enumerator nextObject])\n\t{\n\t\tNSString *filePath = fileURL.path;\n\t\tif ([filePath.lastPathComponent isEqualToString:@\"Info.plist\"]) {\n\t\t\tNSDictionary *infoDict = [NSDictionary dictionaryWithContentsOfFile:filePath];\n\t\t\tif (!infoDict) continue;\n\t\t\tNSString *bundleId = infoDict[@\"CFBundleIdentifier\"];\n\t\t\tNSString *bundleExecutable = infoDict[@\"CFBundleExecutable\"];\n\t\t\tif (!bundleId || !bundleExecutable) continue;\n\t\t\tif ([bundleId isEqualToString:@\"\"] || [bundleExecutable isEqualToString:@\"\"]) continue;\n\t\t\tNSString *bundleMainExecutablePath = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:bundleExecutable];\n\t\t\tif (![[NSFileManager defaultManager] fileExistsAtPath:bundleMainExecutablePath]) continue;\n\n\t\t\tNSString *packageType = infoDict[@\"CFBundlePackageType\"];\n\n\t\t\t// We don't care about frameworks (yet)\n\t\t\tif ([packageType isEqualToString:@\"FMWK\"]) continue;\n\n\t\t\tNSMutableDictionary *entitlementsToUse = dumpEntitlementsFromBinaryAtPath(bundleMainExecutablePath).mutableCopy;\n\t\t\tif (isSameFile(bundleMainExecutablePath, mainExecutablePath)) {\n\t\t\t\t// In the case where the main executable of the app currently has no entitlements at all\n\t\t\t\t// We want to ensure it gets signed with fallback entitlements\n\t\t\t\t// These mimic the entitlements that Xcodes gives every app it signs\n\t\t\t\tif (!entitlementsToUse) {\n\t\t\t\t\tentitlementsToUse = @{\n\t\t\t\t\t\t@\"application-identifier\" : @\"TROLLTROLL.*\",\n\t\t\t\t\t\t@\"com.apple.developer.team-identifier\" : @\"TROLLTROLL\",\n\t\t\t\t\t\t@\"get-task-allow\" : (__bridge id)kCFBooleanTrue,\n\t\t\t\t\t\t@\"keychain-access-groups\" : @[\n\t\t\t\t\t\t\t@\"TROLLTROLL.*\",\n\t\t\t\t\t\t\t@\"com.apple.token\"\n\t\t\t\t\t\t],\n\t\t\t\t\t}.mutableCopy;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!entitlementsToUse) entitlementsToUse = [NSMutableDictionary new];\n\n#ifndef TROLLSTORE_LITE\n\t\t\t// Developer mode does not exist before iOS 16\n\t\t\tif (@available(iOS 16, *)){\n\t\t\t\tif (!requiresDevMode) {\n\t\t\t\t\tfor (NSString* restrictedEntitlementKey in @[\n\t\t\t\t\t\t@\"get-task-allow\", \n\t\t\t\t\t\t@\"task_for_pid-allow\", \n\t\t\t\t\t\t@\"com.apple.system-task-ports\",\n\t\t\t\t\t\t@\"com.apple.system-task-ports.control\",\n\t\t\t\t\t\t@\"com.apple.system-task-ports.token.control\",\n\t\t\t\t\t\t@\"com.apple.private.cs.debugger\"\n\t\t\t\t\t]) {\n\t\t\t\t\t\tNSObject *restrictedEntitlement = entitlementsToUse[restrictedEntitlementKey];\n\t\t\t\t\t\tif (restrictedEntitlement && [restrictedEntitlement isKindOfClass:[NSNumber class]] && [(NSNumber *)restrictedEntitlement boolValue]) {\n\t\t\t\t\t\t\trequiresDevMode = YES;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tNSObject *containerRequiredO = entitlementsToUse[@\"com.apple.private.security.container-required\"];\n\t\t\tBOOL containerRequired = YES;\n\t\t\tif (containerRequiredO && [containerRequiredO isKindOfClass:[NSNumber class]]) {\n\t\t\t\tcontainerRequired = [(NSNumber *)containerRequiredO boolValue];\n\t\t\t}\n\t\t\telse if (containerRequiredO && [containerRequiredO isKindOfClass:[NSString class]]) {\n\t\t\t\t// Keep whatever is in it if it's a string...\n\t\t\t\tcontainerRequired = NO;\n\t\t\t}\n\n\t\t\tif (containerRequired) {\n\t\t\t\tNSObject *noContainerO = entitlementsToUse[@\"com.apple.private.security.no-container\"];\n\t\t\t\tBOOL noContainer = NO;\n\t\t\t\tif (noContainerO && [noContainerO isKindOfClass:[NSNumber class]]) {\n\t\t\t\t\tnoContainer = [(NSNumber *)noContainerO boolValue];\n\t\t\t\t}\n\t\t\t\tNSObject *noSandboxO = entitlementsToUse[@\"com.apple.private.security.no-sandbox\"];\n\t\t\t\tBOOL noSandbox = NO;\n\t\t\t\tif (noSandboxO && [noSandboxO isKindOfClass:[NSNumber class]]) {\n\t\t\t\t\tnoSandbox = [(NSNumber *)noSandboxO boolValue];\n\t\t\t\t}\n\t\t\t\tif (!noContainer && !noSandbox) {\n\t\t\t\t\tentitlementsToUse[@\"com.apple.private.security.container-required\"] = bundleId;\n\t\t\t\t}\n\t\t\t}\n#else\n\t\t\t// Since TrollStore Lite adhoc signs stuff, this means that on PMAP_CS devices, it will run with \"PMAP_CS_IN_LOADED_TRUST_CACHE\" trust level\n\t\t\t// We need to overwrite it so that the app runs as expected (Dopamine 2.1.5+ feature)\n\t\t\tentitlementsToUse[@\"jb.pmap_cs_custom_trust\"] = @\"PMAP_CS_APP_STORE\";\n#endif\n\n\t\t\tint r = signAdhoc(bundleMainExecutablePath, entitlementsToUse);\n\t\t\tif (r != 0) return r;\n\t\t}\n\t}\n\n\t// All entitlement related issues should be fixed at this point, so all we need to do is sign the entire bundle\n\t// And then apply the CoreTrust bypass to all executables\n\t// XXX: This only works because we're using ldid at the moment and that recursively signs everything\n\tint r = signAdhoc(appPath, nil);\n\tif (r != 0) return r;\n\n#ifndef TROLLSTORE_LITE\n\t// Apply CoreTrust bypass\n\tenumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil];\n\twhile(fileURL = [enumerator nextObject])\n\t{\n\t\tNSString *filePath = fileURL.path;\n\t\tFAT *fat = fat_init_from_path(filePath.fileSystemRepresentation);\n\t\tif (fat) {\n\t\t\tNSLog(@\"%@ is binary\", filePath);\n\t\t\tMachO *machoForExtraction = fat_find_preferred_slice(fat);\n\t\t\tif (machoForExtraction) {\n\t\t\t\t// Extract best slice\n\t\t\t\tNSString *tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];\n\t\t\t\tMemoryStream *sliceStream = macho_get_stream(machoForExtraction);\n\t\t\t\tMemoryStream *sliceOutStream = file_stream_init_from_path(tmpPath.fileSystemRepresentation, 0, 0, FILE_STREAM_FLAG_WRITABLE | FILE_STREAM_FLAG_AUTO_EXPAND);\n\t\t\t\tif (sliceOutStream) {\n\t\t\t\t\tmemory_stream_copy_data(sliceStream, 0, sliceOutStream, 0, memory_stream_get_size(sliceStream));\n\t\t\t\t\tmemory_stream_free(sliceOutStream);\n\n\t\t\t\t\t// Now we have the best slice at tmpPath, which we will apply the bypass to, then copy it over the original file\n\t\t\t\t\t// We loose all other slices doing that but they aren't a loss as they wouldn't run either way\n\t\t\t\t\tNSLog(@\"[%@] Applying CoreTrust bypass...\", filePath);\n\t\t\t\t\tint r = apply_coretrust_bypass(tmpPath.fileSystemRepresentation);\n\t\t\t\t\tif (r == 0) {\n\t\t\t\t\t\tNSLog(@\"[%@] Applied CoreTrust bypass!\", filePath);\n\t\t\t\t\t}\n\t\t\t\t\telse if (r == 2) {\n\t\t\t\t\t\tNSLog(@\"[%@] Cannot apply CoreTrust bypass on an encrypted binary!\", filePath);\n\t\t\t\t\t\tif (isSameFile(filePath, mainExecutablePath)) {\n\t\t\t\t\t\t\t// If this is the main binary, this error is fatal\n\t\t\t\t\t\t\tNSLog(@\"[%@] Main binary is encrypted, cannot continue!\", filePath);\n\t\t\t\t\t\t\tfat_free(fat);\n\t\t\t\t\t\t\treturn 180;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t// If not, we can continue but want to show a warning after the app is installed\n\t\t\t\t\t\t\thasAdditionalEncryptedBinaries = YES;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (r == 3) { // Non-fatal - unsupported MachO type\n\t\t\t\t\t\tNSLog(@\"[%@] Cannot apply CoreTrust bypass on an unsupported MachO type!\", filePath);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tNSLog(@\"[%@] CoreTrust bypass failed!!! :(\", filePath);\n\t\t\t\t\t\tfat_free(fat);\n\t\t\t\t\t\treturn 185;\n\t\t\t\t\t}\n\n\t\t\t\t\t// tempFile is now signed, overwrite original file at filePath with it\n\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];\n\t\t\t\t\t[[NSFileManager defaultManager] moveItemAtPath:tmpPath toPath:filePath error:nil];\n\t\t\t\t}\n\t\t\t}\n\t\t\tfat_free(fat);\n\t\t}\n\t}\n\n\tif (requiresDevMode) {\n\t\t// Postpone trying to enable dev mode until after the app is (successfully) installed\n\t\treturn 182;\n\t}\n#else // TROLLSTORE_LITE\n\t// Just check for whether anything is fairplay encrypted\n\tenumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:appPath] includingPropertiesForKeys:nil options:0 errorHandler:nil];\n\twhile(fileURL = [enumerator nextObject])\n\t{\n\t\tNSString *filePath = fileURL.path;\n\t\tFAT *fat = fat_init_from_path(filePath.fileSystemRepresentation);\n\t\tif (fat) {\n\t\t\tNSLog(@\"%@ is binary\", filePath);\n\t\t\tMachO *macho = fat_find_preferred_slice(fat);\n\t\t\tif (macho) {\n\t\t\t\tif (macho_is_encrypted(macho)) {\n\t\t\t\t\tNSLog(@\"[%@] Cannot apply CoreTrust bypass on an encrypted binary!\", filePath);\n\t\t\t\t\tif (isSameFile(filePath, mainExecutablePath)) {\n\t\t\t\t\t\t// If this is the main binary, this error is fatal\n\t\t\t\t\t\tNSLog(@\"[%@] Main binary is encrypted, cannot continue!\", filePath);\n\t\t\t\t\t\tfat_free(fat);\n\t\t\t\t\t\treturn 180;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// If not, we can continue but want to show a warning after the app is installed\n\t\t\t\t\t\thasAdditionalEncryptedBinaries = YES;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfat_free(fat);\n\t\t}\n\t}\n#endif\n\n\tif (hasAdditionalEncryptedBinaries) {\n\t\treturn 184;\n\t}\n\n\treturn 0;\n}\n#endif\n\nvoid applyPatchesToInfoDictionary(NSString* appPath)\n{\n\tNSURL* appURL = [NSURL fileURLWithPath:appPath];\n\tNSURL* infoPlistURL = [appURL URLByAppendingPathComponent:@\"Info.plist\"];\n\tNSMutableDictionary* infoDictM = [[NSDictionary dictionaryWithContentsOfURL:infoPlistURL error:nil] mutableCopy];\n\tif(!infoDictM) return;\n\n\t// Enable Notifications\n\tinfoDictM[@\"SBAppUsesLocalNotifications\"] = @1;\n\n\t// Remove system claimed URL schemes if existant\n\tNSSet* appleSchemes = systemURLSchemes();\n\tNSArray* CFBundleURLTypes = infoDictM[@\"CFBundleURLTypes\"];\n\tif([CFBundleURLTypes isKindOfClass:[NSArray class]])\n\t{\n\t\tNSMutableArray* CFBundleURLTypesM = [NSMutableArray new];\n\n\t\tfor(NSDictionary* URLType in CFBundleURLTypes)\n\t\t{\n\t\t\tif(![URLType isKindOfClass:[NSDictionary class]]) continue;\n\n\t\t\tNSMutableDictionary* modifiedURLType = URLType.mutableCopy;\n\t\t\tNSArray* URLSchemes = URLType[@\"CFBundleURLSchemes\"];\n\t\t\tif(URLSchemes)\n\t\t\t{\n\t\t\t\tNSMutableSet* URLSchemesSet = [NSMutableSet setWithArray:URLSchemes];\n\t\t\t\tfor(NSString* existingURLScheme in [URLSchemesSet copy])\n\t\t\t\t{\n\t\t\t\t\tif(![existingURLScheme isKindOfClass:[NSString class]])\n\t\t\t\t\t{\n\t\t\t\t\t\t[URLSchemesSet removeObject:existingURLScheme];\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif([appleSchemes containsObject:existingURLScheme.lowercaseString])\n\t\t\t\t\t{\n\t\t\t\t\t\t[URLSchemesSet removeObject:existingURLScheme];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmodifiedURLType[@\"CFBundleURLSchemes\"] = [URLSchemesSet allObjects];\n\t\t\t}\n\t\t\t[CFBundleURLTypesM addObject:modifiedURLType.copy];\n\t\t}\n\n\t\tinfoDictM[@\"CFBundleURLTypes\"] = CFBundleURLTypesM.copy;\n\t}\n\n\t[infoDictM writeToURL:infoPlistURL error:nil];\n}\n\n// 170: failed to create container for app bundle\n// 171: a non trollstore app with the same identifier is already installled\n// 172: no info.plist found in app\n// 173: app is not signed and cannot be signed because ldid not installed or didn't work\n// 174: \n// 180: tried to sign app where the main binary is encrypted\n// 184: tried to sign app where an additional binary is encrypted\n\nint installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate, BOOL useInstalldMethod, BOOL skipUICache)\n{\n\tNSLog(@\"[installApp force = %d]\", force);\n\n\tNSString* appPayloadPath = [appPackagePath stringByAppendingPathComponent:@\"Payload\"];\n\n\tNSString* appBundleToInstallPath = findAppPathInBundlePath(appPayloadPath);\n\tif(!appBundleToInstallPath) return 167;\n\n\tNSString* appId = appIdForAppPath(appBundleToInstallPath);\n\tif(!appId) return 176;\n\n\tif(([appId.lowercaseString isEqualToString:@\"com.opa334.trollstore\"] && !isTSUpdate) || [immutableAppBundleIdentifiers() containsObject:appId.lowercaseString])\n\t{\n\t\treturn 179;\n\t}\n\n\tif(!infoDictionaryForAppPath(appBundleToInstallPath)) return 172;\n\n\tif(!isTSUpdate)\n\t{\n\t\tapplyPatchesToInfoDictionary(appBundleToInstallPath);\n\t}\n\n\tBOOL requiresDevMode = NO;\n\tBOOL hasAdditionalEncryptedBinaries = NO;\n\n\tif(sign)\n\t{\n\t\tint signRet = signApp(appBundleToInstallPath);\n\t\t// 182: app requires developer mode; non-fatal\n\t\t// 184: app has additional encrypted binaries; non-fatal\n\t\tif(signRet != 0) {\n\t\t\tif (signRet == 182) {\n\t\t\t\trequiresDevMode = YES;\n\t\t\t} else if (signRet == 184) {\n\t\t\t\thasAdditionalEncryptedBinaries = YES;\n\t\t\t} else {\n\t\t\t\treturn signRet;\n\t\t\t}\n\t\t};\n\t}\n\n\tMCMAppContainer* appContainer = [MCMAppContainer containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil];\n\tif(appContainer)\n\t{\n\t\t// App update\n\t\t// Replace existing bundle with new version\n\n\t\t// Check if the existing app bundle is empty\n\t\tNSURL* bundleContainerURL = appContainer.url;\n\t\tNSURL* appBundleURL = findAppURLInBundleURL(bundleContainerURL);\n\n\t\t// Make sure the installed app is a TrollStore app or the container is empty (or the force flag is set)\n\t\tNSURL* trollStoreMarkURL = [bundleContainerURL URLByAppendingPathComponent:TS_ACTIVE_MARKER];\n\t\tif(appBundleURL && ![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil] && !force)\n\t\t{\n\t\t\tNSLog(@\"[installApp] already installed and not a TrollStore app... bailing out\");\n\t\t\treturn 171;\n\t\t}\n\t\telse if (appBundleURL) {\n\t\t\t// When overwriting an app that has been installed with a different TrollStore flavor, make sure to remove the marker of said flavor\n\t\t\tNSURL *otherMarkerURL = [bundleContainerURL URLByAppendingPathComponent:TS_INACTIVE_MARKER];\n\t\t\tif ([otherMarkerURL checkResourceIsReachableAndReturnError:nil]) {\n\t\t\t\t[[NSFileManager defaultManager] removeItemAtURL:otherMarkerURL error:nil];\n\t\t\t}\n\t\t}\n\n\t\t// Terminate app if it's still running\n\t\tif(!isTSUpdate)\n\t\t{\n\t\t\tBKSTerminateApplicationForReasonAndReportWithDescription(appId, 5, false, @\"TrollStore - App updated\");\n\t\t}\n\n\t\tNSLog(@\"[installApp] replacing existing app with new version\");\n\n\t\t// Delete existing .app directory if it exists\n\t\tif(appBundleURL)\n\t\t{\n\t\t\t[[NSFileManager defaultManager] removeItemAtURL:appBundleURL error:nil];\n\t\t}\n\n\t\tNSString* newAppBundlePath = [bundleContainerURL.path stringByAppendingPathComponent:appBundleToInstallPath.lastPathComponent];\n\t\tNSLog(@\"[installApp] new app path: %@\", newAppBundlePath);\n\n\t\t// Install new version into existing app bundle\n\t\tNSError* copyError;\n\t\tBOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appBundleToInstallPath toPath:newAppBundlePath error:&copyError];\n\t\tif(!suc)\n\t\t{\n\t\t\tNSLog(@\"[installApp] Error copying new version during update: %@\", copyError);\n\t\t\treturn 178;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Initial app install\n\t\tBOOL systemMethodSuccessful = NO;\n\t\tif(useInstalldMethod)\n\t\t{\n\t\t\t// System method\n\t\t\t// Do initial placeholder installation using LSApplicationWorkspace\n\t\t\tNSLog(@\"[installApp] doing placeholder installation using LSApplicationWorkspace\");\n\n\t\t\t// The installApplication API (re)moves the app bundle, so in order to be able to later \n\t\t\t// fall back to the custom method, we need to make a temporary copy just for using it on this API once\n\t\t\t// Yeah this sucks, but there is no better solution unfortunately\n\t\t\tNSError* tmpCopyError;\n\t\t\tNSString* lsAppPackageTmpCopy = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];\n\t\t\tif(![[NSFileManager defaultManager] copyItemAtPath:appPackagePath toPath:lsAppPackageTmpCopy error:&tmpCopyError])\n\t\t\t{\n\t\t\t\tNSLog(@\"failed to make temporary copy of app packge: %@\", tmpCopyError);\n\t\t\t\treturn 170;\n\t\t\t}\n\n\t\t\tNSError* installError;\n\t\t\t@try\n\t\t\t{\n\t\t\t\tsystemMethodSuccessful = [[LSApplicationWorkspace defaultWorkspace] installApplication:[NSURL fileURLWithPath:lsAppPackageTmpCopy] withOptions:@{\n\t\t\t\t\tLSInstallTypeKey : @1,\n\t\t\t\t\t@\"PackageType\" : @\"Placeholder\"\n\t\t\t\t} error:&installError];\n\t\t\t}\n\t\t\t@catch(NSException* e)\n\t\t\t{\n\t\t\t\tNSLog(@\"[installApp] encountered expection %@ while trying to do placeholder install\", e);\n\t\t\t\tsystemMethodSuccessful = NO;\n\t\t\t}\n\n\t\t\tif(!systemMethodSuccessful)\n\t\t\t{\n\t\t\t\tNSLog(@\"[installApp] encountered error %@ while trying to do placeholder install\", installError);\n\t\t\t}\n\n\t\t\t[[NSFileManager defaultManager] removeItemAtPath:lsAppPackageTmpCopy error:nil];\n\t\t}\n\n\t\tif(!systemMethodSuccessful)\n\t\t{\n\t\t\t// Custom method\n\t\t\t// Manually create app bundle via MCM apis and move app there\n\t\t\tNSLog(@\"[installApp] doing custom installation using MCMAppContainer\");\n\n\t\t\tNSError* mcmError;\n\t\t\tappContainer = [MCMAppContainer containerWithIdentifier:appId createIfNecessary:YES existed:nil error:&mcmError];\n\n\t\t\tif(!appContainer || mcmError)\n\t\t\t{\n\t\t\t\tNSLog(@\"[installApp] failed to create app container for %@: %@\", appId, mcmError);\n\t\t\t\treturn 170;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tNSLog(@\"[installApp] created app container: %@\", appContainer);\n\t\t\t}\n\n\t\t\tNSString* newAppBundlePath = [appContainer.url.path stringByAppendingPathComponent:appBundleToInstallPath.lastPathComponent];\n\t\t\tNSLog(@\"[installApp] new app path: %@\", newAppBundlePath);\n\t\t\t\n\t\t\tNSError* copyError;\n\t\t\tBOOL suc = [[NSFileManager defaultManager] copyItemAtPath:appBundleToInstallPath toPath:newAppBundlePath error:&copyError];\n\t\t\tif(!suc)\n\t\t\t{\n\t\t\t\tNSLog(@\"[installApp] Failed to copy app bundle for app %@, error: %@\", appId, copyError);\n\t\t\t\treturn 178;\n\t\t\t}\n\t\t}\n\t}\n\n\tappContainer = [MCMAppContainer containerWithIdentifier:appId createIfNecessary:NO existed:nil error:nil];\n\n\t// Mark app as TrollStore app\n\tNSURL* trollStoreMarkURL = [appContainer.url URLByAppendingPathComponent:TS_ACTIVE_MARKER];\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:trollStoreMarkURL.path])\n\t{\n\t\tNSError* creationError;\n\t\tNSData* emptyData = [NSData data];\n\t\tBOOL marked = [emptyData writeToURL:trollStoreMarkURL options:0 error:&creationError];\n\t\tif(!marked)\n\t\t{\n\t\t\tNSLog(@\"[installApp] failed to mark %@ as TrollStore app by creating %@, error: %@\", appId, trollStoreMarkURL.path, creationError);\n\t\t\treturn 177;\n\t\t}\n\t}\n\n\t// At this point the (new version of the) app is installed but still needs to be registered\n\t// Also permissions need to be fixed\n\tNSURL* updatedAppURL = findAppURLInBundleURL(appContainer.url);\n\tfixPermissionsOfAppBundle(updatedAppURL.path);\n\tif (!skipUICache) {\n\t\tif (!registerPath(updatedAppURL.path, 0, !shouldRegisterAsUserByDefault())) {\n\t\t\t[[NSFileManager defaultManager] removeItemAtURL:appContainer.url error:nil];\n\t\t\treturn 181;\n\t\t}\n\t}\n\n\t// Handle developer mode after installing and registering the app, to ensure that we\n\t// don't arm developer mode but then fail to install the app\n\tif (requiresDevMode) {\n\t\tBOOL alreadyEnabled = NO;\n\t\tif (armDeveloperMode(&alreadyEnabled)) {\n\t\t\tif (!alreadyEnabled) {\n\t\t\t\tNSLog(@\"[installApp] app requires developer mode and we have successfully armed it\");\n\t\t\t\t// non-fatal\n\t\t\t\treturn 182;\n\t\t\t}\n\t\t} else {\n\t\t\tNSLog(@\"[installApp] failed to arm developer mode\");\n\t\t\t// fatal\n\t\t\treturn 183;\n\t\t}\n\t}\n\n\tif (hasAdditionalEncryptedBinaries) {\n\t\tNSLog(@\"[installApp] app has additional encrypted binaries\");\n\t\t// non-fatal\n\t\treturn 184;\n\t}\n\n\treturn 0;\n}\n\nint uninstallApp(NSString* appPath, NSString* appId, BOOL useCustomMethod)\n{\n\tBOOL deleteSuc = NO;\n\tif(!appId && appPath)\n\t{\n\t\t// Special case, something is wrong about this app\n\t\t// Most likely the Info.plist is missing\n\t\t// (Hopefully this never happens)\n\t\tdeleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:nil];\n\t\tregisterPath(appPath, YES, YES);\n\t\treturn 0;\n\t}\n\n\tif(appId)\n\t{\n\t\tLSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForIdentifier:appId];\n\n\t\t// delete data container\n\t\tif (appProxy.dataContainerURL) {\n\t\t\t[[NSFileManager defaultManager] removeItemAtURL:appProxy.dataContainerURL error:nil];\n\t\t}\n\n\t\t// delete group container paths\n\t\t[[appProxy groupContainerURLs] enumerateKeysAndObjectsUsingBlock:^(NSString* groupId, NSURL* groupURL, BOOL* stop)\n\t\t{\n\t\t\t// If another app still has this group, don't delete it\n\t\t\tNSArray<LSApplicationProxy*>* appsWithGroup = applicationsWithGroupId(groupId);\n\t\t\tif(appsWithGroup.count > 1)\n\t\t\t{\n\t\t\t\tNSLog(@\"[uninstallApp] not deleting %@, appsWithGroup.count:%lu\", groupURL, appsWithGroup.count);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNSLog(@\"[uninstallApp] deleting %@\", groupURL);\n\t\t\t[[NSFileManager defaultManager] removeItemAtURL:groupURL error:nil];\n\t\t}];\n\n\t\t// delete app plugin paths\n\t\tfor(LSPlugInKitProxy* pluginProxy in appProxy.plugInKitPlugins)\n\t\t{\n\t\t\tNSURL* pluginURL = pluginProxy.dataContainerURL;\n\t\t\tif(pluginURL)\n\t\t\t{\n\t\t\t\tNSLog(@\"[uninstallApp] deleting %@\", pluginURL);\n\t\t\t\t[[NSFileManager defaultManager] removeItemAtURL:pluginURL error:nil];\n\t\t\t}\n\t\t}\n\n\t\tBOOL systemMethodSuccessful = NO;\n\t\tif(!useCustomMethod)\n\t\t{\n\t\t\tsystemMethodSuccessful = [[LSApplicationWorkspace defaultWorkspace] uninstallApplication:appId withOptions:nil];\n\t\t}\n\n\t\tif(!systemMethodSuccessful)\n\t\t{\n\t\t\tdeleteSuc = [[NSFileManager defaultManager] removeItemAtPath:[appPath stringByDeletingLastPathComponent] error:nil];\n\t\t\tregisterPath(appPath, YES, YES);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdeleteSuc = systemMethodSuccessful;\n\t\t}\n\t}\n\n\tif(deleteSuc)\n\t{\n\t\tcleanRestrictions();\n\t\treturn 0;\n\t}\n\telse\n\t{\n\t\treturn 1;\n\t}\n}\n\nint uninstallAppByPath(NSString* appPath, BOOL useCustomMethod)\n{\n\tif(!appPath) return 1;\n\n\tNSString* standardizedAppPath = appPath.stringByStandardizingPath;\n\n\tif(![standardizedAppPath hasPrefix:@\"/var/containers/Bundle/Application/\"] && standardizedAppPath.pathComponents.count == 5)\n\t{\n\t\treturn 1;\n\t}\n\n\tNSString* appId = appIdForAppPath(standardizedAppPath);\n\treturn uninstallApp(appPath, appId, useCustomMethod);\n}\n\nint uninstallAppById(NSString* appId, BOOL useCustomMethod)\n{\n\tif(!appId) return 1;\n\tNSString* appPath = appPathForAppId(appId);\n\tif(!appPath) return 1;\n\treturn uninstallApp(appPath, appId, useCustomMethod);\n}\n\n// 166: IPA does not exist or is not accessible\n// 167: IPA does not appear to contain an app\n// 180: IPA's main binary is encrypted\n// 184: IPA contains additional encrypted binaries\nint installIpa(NSString* ipaPath, BOOL force, BOOL useInstalldMethod, BOOL skipUICache)\n{\n\tcleanRestrictions();\n\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:ipaPath]) return 166;\n\n\tBOOL suc = NO;\n\tNSString* tmpPackagePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];\n\t\n\tsuc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPackagePath withIntermediateDirectories:NO attributes:nil error:nil];\n\tif(!suc) return 1;\n\n\tint extractRet = extract(ipaPath, tmpPackagePath);\n\tif(extractRet != 0)\n\t{\n\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil];\n\t\treturn 168;\n\t}\n\n\tint ret = installApp(tmpPackagePath, YES, force, NO, useInstalldMethod, skipUICache);\n\t\n\t[[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil];\n\n\treturn ret;\n}\n\nvoid uninstallAllApps(BOOL useCustomMethod)\n{\n\tfor(NSString* appPath in trollStoreInstalledAppBundlePaths())\n\t{\n\t\tuninstallAppById(appIdForAppPath(appPath), useCustomMethod);\n\t}\n}\n\nint uninstallTrollStore(BOOL unregister)\n{\n\tNSString* trollStore = trollStorePath();\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:trollStore]) return NO;\n\n\tif(unregister)\n\t{\n\t\tregisterPath(trollStoreAppPath(), YES, YES);\n\t}\n\n\treturn [[NSFileManager defaultManager] removeItemAtPath:trollStore error:nil];\n}\n\nint installTrollStore(NSString* pathToTar)\n{\n\t_CFPreferencesSetValueWithContainerType _CFPreferencesSetValueWithContainer = (_CFPreferencesSetValueWithContainerType)dlsym(RTLD_DEFAULT, \"_CFPreferencesSetValueWithContainer\");\n\t_CFPreferencesSynchronizeWithContainerType _CFPreferencesSynchronizeWithContainer = (_CFPreferencesSynchronizeWithContainerType)dlsym(RTLD_DEFAULT, \"_CFPreferencesSynchronizeWithContainer\");\n\t_CFPreferencesSetValueWithContainer(CFSTR(\"SBShowNonDefaultSystemApps\"), kCFBooleanTrue, CFSTR(\"com.apple.springboard\"), CFSTR(\"mobile\"), kCFPreferencesAnyHost, kCFPreferencesNoContainer);\n\t_CFPreferencesSynchronizeWithContainer(CFSTR(\"com.apple.springboard\"), CFSTR(\"mobile\"), kCFPreferencesAnyHost, kCFPreferencesNoContainer);\n\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:pathToTar]) return 1;\n\tif(![pathToTar.pathExtension isEqualToString:@\"tar\"]) return 1;\n\n\tNSString* tmpPackagePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];\n\tNSString* tmpPayloadPath = [tmpPackagePath stringByAppendingPathComponent:@\"Payload\"];\n\tBOOL suc = [[NSFileManager defaultManager] createDirectoryAtPath:tmpPayloadPath withIntermediateDirectories:YES attributes:nil error:nil];\n\tif(!suc) return 1;\n\n\tint extractRet = extract(pathToTar, tmpPayloadPath);\n\tif(extractRet != 0)\n\t{\n\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil];\n\t\treturn 169;\n\t}\n\n\tNSString* tmpTrollStorePath = [tmpPayloadPath stringByAppendingPathComponent:@\"TrollStore.app\"];\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:tmpTrollStorePath]) return 1;\n\n\t//if (@available(iOS 16, *)) {} else {\n\t\t// Transfer existing ldid installation if it exists\n\t\t// But only if the to-be-installed version of TrollStore is 1.5.0 or above\n\t\t// This is to make it possible to downgrade to older versions still\n\n\t\tNSString* toInstallInfoPlistPath = [tmpTrollStorePath stringByAppendingPathComponent:@\"Info.plist\"];\n\t\tif(![[NSFileManager defaultManager] fileExistsAtPath:toInstallInfoPlistPath]) return 1;\n\n\t\tNSDictionary* toInstallInfoDict = [NSDictionary dictionaryWithContentsOfFile:toInstallInfoPlistPath];\n\t\tNSString* toInstallVersion = toInstallInfoDict[@\"CFBundleVersion\"];\n\n\t\tNSComparisonResult result = [@\"1.5.0\" compare:toInstallVersion options:NSNumericSearch];\n\t\tif(result != NSOrderedDescending)\n\t\t{\n\t\t\tNSString* existingLdidPath = [trollStoreAppPath() stringByAppendingPathComponent:@\"ldid\"];\n\t\t\tNSString* existingLdidVersionPath = [trollStoreAppPath() stringByAppendingPathComponent:@\"ldid.version\"];\n\t\t\tif([[NSFileManager defaultManager] fileExistsAtPath:existingLdidPath])\n\t\t\t{\n\t\t\t\tNSString* tmpLdidPath = [tmpTrollStorePath stringByAppendingPathComponent:@\"ldid\"];\n\t\t\t\tif(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidPath])\n\t\t\t\t{\n\t\t\t\t\t[[NSFileManager defaultManager] copyItemAtPath:existingLdidPath toPath:tmpLdidPath error:nil];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif([[NSFileManager defaultManager] fileExistsAtPath:existingLdidVersionPath])\n\t\t\t{\n\t\t\t\tNSString* tmpLdidVersionPath = [tmpTrollStorePath stringByAppendingPathComponent:@\"ldid.version\"];\n\t\t\t\tif(![[NSFileManager defaultManager] fileExistsAtPath:tmpLdidVersionPath])\n\t\t\t\t{\n\t\t\t\t\t[[NSFileManager defaultManager] copyItemAtPath:existingLdidVersionPath toPath:tmpLdidVersionPath error:nil];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t//}\n\n\t// Merge existing URL scheme settings value\n\tif(!getTSURLSchemeState(nil))\n\t{\n\t\tsetTSURLSchemeState(NO, tmpTrollStorePath);\n\t}\n\n\t// Update system app persistence helper if used\n\tLSApplicationProxy* persistenceHelperApp = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_SYSTEM);\n\tif(persistenceHelperApp)\n\t{\n\t\tNSString* trollStorePersistenceHelper = [tmpTrollStorePath stringByAppendingPathComponent:@\"PersistenceHelper\"];\n\t\tNSString* trollStoreRootHelper = [tmpTrollStorePath stringByAppendingPathComponent:@\"trollstorehelper\"];\n\t\t_installPersistenceHelper(persistenceHelperApp, trollStorePersistenceHelper, trollStoreRootHelper);\n\t}\n\n\tint ret = installApp(tmpPackagePath, NO, YES, YES, YES, NO);\n\tNSLog(@\"[installTrollStore] installApp => %d\", ret);\n\t[[NSFileManager defaultManager] removeItemAtPath:tmpPackagePath error:nil];\n\treturn ret;\n}\n\nvoid refreshAppRegistrations(BOOL system)\n{\n\tregisterPath(trollStoreAppPath(), NO, system);\n\n\t// the reason why there is even an option to register everything as user\n\t// is because it fixes an issue where app permissions would reset during an icon cache reload\n\tfor(NSString* appPath in trollStoreInstalledAppBundlePaths())\n\t{\n\t\tregisterPath(appPath, NO, system);\n\t}\n}\n\nBOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePersistenceHelper, NSString* sourceRootHelper)\n{\n\tNSLog(@\"_installPersistenceHelper(%@, %@, %@)\", appProxy, sourcePersistenceHelper, sourceRootHelper);\n\n\tNSString* executablePath = appProxy.canonicalExecutablePath;\n\tNSString* bundlePath = appProxy.bundleURL.path;\n\tif(!executablePath)\n\t{\n\t\tNSBundle* appBundle = [NSBundle bundleWithPath:bundlePath];\n\t\texecutablePath = [bundlePath stringByAppendingPathComponent:[appBundle objectForInfoDictionaryKey:@\"CFBundleExecutable\"]];\n\t}\n\n\tNSString* markPath = [bundlePath stringByAppendingPathComponent:@\".TrollStorePersistenceHelper\"];\n\tNSString* rootHelperPath = [bundlePath stringByAppendingPathComponent:@\"trollstorehelper\"];\n\n\t// remove existing persistence helper binary if exists\n\tif([[NSFileManager defaultManager] fileExistsAtPath:markPath] && [[NSFileManager defaultManager] fileExistsAtPath:executablePath])\n\t{\n\t\t[[NSFileManager defaultManager] removeItemAtPath:executablePath error:nil];\n\t}\n\n\t// remove existing root helper binary if exists\n\tif([[NSFileManager defaultManager] fileExistsAtPath:rootHelperPath])\n\t{\n\t\t[[NSFileManager defaultManager] removeItemAtPath:rootHelperPath error:nil];\n\t}\n\n\t// install new persistence helper binary\n\tif(![[NSFileManager defaultManager] copyItemAtPath:sourcePersistenceHelper toPath:executablePath error:nil])\n\t{\n\t\treturn NO;\n\t}\n\n\tchmod(executablePath.fileSystemRepresentation, 0755);\n\tchown(executablePath.fileSystemRepresentation, 33, 33);\n\n\tNSError* error;\n\tif(![[NSFileManager defaultManager] copyItemAtPath:sourceRootHelper toPath:rootHelperPath error:&error])\n\t{\n\t\tNSLog(@\"error copying root helper: %@\", error);\n\t}\n\n\tchmod(rootHelperPath.fileSystemRepresentation, 0755);\n\tchown(rootHelperPath.fileSystemRepresentation, 0, 0);\n\n\t// mark system app as persistence helper\n\tif(![[NSFileManager defaultManager] fileExistsAtPath:markPath])\n\t{\n\t\t[[NSFileManager defaultManager] createFileAtPath:markPath contents:[NSData data] attributes:nil];\n\t}\n\n\treturn YES;\n}\n\nvoid installPersistenceHelper(NSString* systemAppId, NSString *persistenceHelperBinary, NSString *rootHelperBinary)\n{\n\tif(findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_ALL)) return;\n\n\tif (persistenceHelperBinary == nil) {\n\t\tpersistenceHelperBinary = [trollStoreAppPath() stringByAppendingPathComponent:@\"PersistenceHelper\"];\n\t}\n\tif (rootHelperBinary == nil) {\n\t\trootHelperBinary = [trollStoreAppPath() stringByAppendingPathComponent:@\"trollstorehelper\"];\n\t}\n\tLSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForIdentifier:systemAppId];\n\tif(!appProxy || ![appProxy.bundleType isEqualToString:@\"System\"]) return;\n\n\tNSString* executablePath = appProxy.canonicalExecutablePath;\n\tNSString* bundlePath = appProxy.bundleURL.path;\n\tNSString* backupPath = [bundlePath stringByAppendingPathComponent:[[executablePath lastPathComponent] stringByAppendingString:@\"_TROLLSTORE_BACKUP\"]];\n\n\tif([[NSFileManager defaultManager] fileExistsAtPath:backupPath]) return;\n\n\tif(![[NSFileManager defaultManager] moveItemAtPath:executablePath toPath:backupPath error:nil]) return;\n\n\tif(!_installPersistenceHelper(appProxy, persistenceHelperBinary, rootHelperBinary))\n\t{\n\t\t[[NSFileManager defaultManager] moveItemAtPath:backupPath toPath:executablePath error:nil];\n\t\treturn;\n\t}\n\n\tBKSTerminateApplicationForReasonAndReportWithDescription(systemAppId, 5, false, @\"TrollStore - Reload persistence helper\");\n}\n\nvoid unregisterUserPersistenceHelper()\n{\n\tLSApplicationProxy* userAppProxy = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_USER);\n\tif(userAppProxy)\n\t{\n\t\tNSString* markPath = [userAppProxy.bundleURL.path stringByAppendingPathComponent:@\".TrollStorePersistenceHelper\"];\n\t\t[[NSFileManager defaultManager] removeItemAtPath:markPath error:nil];\n\t}\n}\n\nvoid uninstallPersistenceHelper(void)\n{\n\tLSApplicationProxy* systemAppProxy = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_SYSTEM);\n\tif(systemAppProxy)\n\t{\n\t\tNSString* executablePath = systemAppProxy.canonicalExecutablePath;\n\t\tNSString* bundlePath = systemAppProxy.bundleURL.path;\n\t\tNSString* backupPath = [bundlePath stringByAppendingPathComponent:[[executablePath lastPathComponent] stringByAppendingString:@\"_TROLLSTORE_BACKUP\"]];\n\t\tif(![[NSFileManager defaultManager] fileExistsAtPath:backupPath]) return;\n\n\t\tNSString* helperPath = [bundlePath stringByAppendingPathComponent:@\"trollstorehelper\"];\n\t\tNSString* markPath = [bundlePath stringByAppendingPathComponent:@\".TrollStorePersistenceHelper\"];\n\n\t\t[[NSFileManager defaultManager] removeItemAtPath:executablePath error:nil];\n\t\t[[NSFileManager defaultManager] removeItemAtPath:markPath error:nil];\n\t\t[[NSFileManager defaultManager] removeItemAtPath:helperPath error:nil];\n\n\t\t[[NSFileManager defaultManager] moveItemAtPath:backupPath toPath:executablePath error:nil];\n\n\t\tBKSTerminateApplicationForReasonAndReportWithDescription(systemAppProxy.bundleIdentifier, 5, false, @\"TrollStore - Reload persistence helper\");\n\t}\n\n\tLSApplicationProxy* userAppProxy = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_USER);\n\tif(userAppProxy)\n\t{\n\t\tunregisterUserPersistenceHelper();\n\t}\n}\n\nvoid registerUserPersistenceHelper(NSString* userAppId)\n{\n\tif(findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_ALL)) return;\n\n\tLSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForIdentifier:userAppId];\n\tif(!appProxy || ![appProxy.bundleType isEqualToString:@\"User\"]) return;\n\n\tNSString* markPath = [appProxy.bundleURL.path stringByAppendingPathComponent:@\".TrollStorePersistenceHelper\"];\n\t[[NSFileManager defaultManager] createFileAtPath:markPath contents:[NSData data] attributes:nil];\n}\n\n// Apparently there is some odd behaviour where TrollStore installed apps sometimes get restricted\n// This works around that issue at least and is triggered when rebuilding icon cache\nvoid cleanRestrictions(void)\n{\n\tNSString* clientTruthPath = @\"/private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/ClientTruth.plist\";\n\tNSURL* clientTruthURL = [NSURL fileURLWithPath:clientTruthPath];\n\tNSDictionary* clientTruthDictionary = [NSDictionary dictionaryWithContentsOfURL:clientTruthURL];\n\n\tif(!clientTruthDictionary) return;\n\n\tNSArray* valuesArr;\n\n\tNSDictionary* lsdAppRemoval = clientTruthDictionary[@\"com.apple.lsd.appremoval\"];\n\tif(lsdAppRemoval && [lsdAppRemoval isKindOfClass:NSDictionary.class])\n\t{\n\t\tNSDictionary* clientRestrictions = lsdAppRemoval[@\"clientRestrictions\"];\n\t\tif(clientRestrictions && [clientRestrictions isKindOfClass:NSDictionary.class])\n\t\t{\n\t\t\tNSDictionary* unionDict = clientRestrictions[@\"union\"];\n\t\t\tif(unionDict && [unionDict isKindOfClass:NSDictionary.class])\n\t\t\t{\n\t\t\t\tNSDictionary* removedSystemAppBundleIDs = unionDict[@\"removedSystemAppBundleIDs\"];\n\t\t\t\tif(removedSystemAppBundleIDs && [removedSystemAppBundleIDs isKindOfClass:NSDictionary.class])\n\t\t\t\t{\n\t\t\t\t\tvaluesArr = removedSystemAppBundleIDs[@\"values\"];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif(!valuesArr || !valuesArr.count) return;\n\n\tNSMutableArray* valuesArrM = valuesArr.mutableCopy;\n\t__block BOOL changed = NO;\n\n\t[valuesArrM enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString* value, NSUInteger idx, BOOL *stop)\n\t{\n\t\tif(!isRemovableSystemApp(value))\n\t\t{\n\t\t\t[valuesArrM removeObjectAtIndex:idx];\n\t\t\tchanged = YES;\n\t\t}\n\t}];\n\n\tif(!changed) return;\n\n\tNSMutableDictionary* clientTruthDictionaryM = (__bridge_transfer NSMutableDictionary*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)clientTruthDictionary, kCFPropertyListMutableContainersAndLeaves);\n\t\n\tclientTruthDictionaryM[@\"com.apple.lsd.appremoval\"][@\"clientRestrictions\"][@\"union\"][@\"removedSystemAppBundleIDs\"][@\"values\"] = valuesArrM;\n\n\t[clientTruthDictionaryM writeToURL:clientTruthURL error:nil];\n\n\tkillall(@\"profiled\", NO); // profiled needs to restart for the changes to apply\n}\n\nint MAIN_NAME(int argc, char *argv[], char *envp[])\n{\n\t@autoreleasepool {\n\t\tif(argc <= 1) return -1;\n\n\t\tif(getuid() != 0)\n\t\t{\n\t\t\tNSLog(@\"ERROR: trollstorehelper has to be run as root.\");\n\t\t\treturn -1;\n\t\t}\n\n\t\tNSMutableArray* args = [NSMutableArray new];\n\t\tfor (int i = 1; i < argc; i++)\n\t\t{\n\t\t\t[args addObject:[NSString stringWithUTF8String:argv[i]]];\n\t\t}\n\n\t\tNSLog(@\"trollstorehelper invoked with arguments: %@\", args);\n\n\t\tint ret = 0;\n\t\tNSString* cmd = args.firstObject;\n\t\tif([cmd isEqualToString:@\"install\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\t// use system method when specified, otherwise use custom method\n\t\t\tBOOL useInstalldMethod = [args containsObject:@\"installd\"];\n\t\t\tBOOL force = [args containsObject:@\"force\"];\n\t\t\tBOOL skipUICache = [args containsObject:@\"skip-uicache\"];\n\t\t\tNSString* ipaPath = args.lastObject;\n\t\t\tret = installIpa(ipaPath, force, useInstalldMethod, skipUICache);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"uninstall\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\t// use custom method when specified, otherwise use system method\n\t\t\tBOOL useCustomMethod = [args containsObject:@\"custom\"];\n\t\t\tNSString* appId = args.lastObject;\n\t\t\tret = uninstallAppById(appId, useCustomMethod);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"uninstall-path\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\t// use custom method when specified, otherwise use system method\n\t\t\tBOOL useCustomMethod = [args containsObject:@\"custom\"];\n\t\t\tNSString* appPath = args.lastObject;\n\t\t\tret = uninstallAppByPath(appPath, useCustomMethod);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"refresh\"])\n\t\t{\n\t\t\trefreshAppRegistrations(!shouldRegisterAsUserByDefault());\n\t\t}\n\t\telse if([cmd isEqualToString:@\"refresh-all\"])\n\t\t{\n\t\t\tcleanRestrictions();\n\t\t\t//refreshAppRegistrations(NO); // <- fixes app permissions resetting, causes apps to move around on home screen, so I had to disable it\n\t\t\t[[NSFileManager defaultManager] removeItemAtPath:@\"/var/containers/Shared/SystemGroup/systemgroup.com.apple.lsd.iconscache/Library/Caches/com.apple.IconsCache\" error:nil];\n\t\t\t[[LSApplicationWorkspace defaultWorkspace] _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:YES];\n\t\t\tif (!shouldRegisterAsUserByDefault()) refreshAppRegistrations(YES);\n\t\t\tkillall(@\"backboardd\", YES);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"url-scheme\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\tNSString* modifyArg = args.lastObject;\n\t\t\tBOOL newState = [modifyArg isEqualToString:@\"enable\"];\n\t\t\tif(newState == YES || [modifyArg isEqualToString:@\"disable\"])\n\t\t\t{\n\t\t\t\tsetTSURLSchemeState(newState, nil);\n\t\t\t}\n\t\t}\n\t\telse if([cmd isEqualToString:@\"reboot\"])\n\t\t{\n\t\t\t[[FBSSystemService sharedService] reboot];\n\t\t\t// Give the system some time to reboot\n\t\t\tsleep(1);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"enable-jit\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\tNSString* userAppId = args.lastObject;\n\t\t\tret = enableJIT(userAppId);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"modify-registration\"])\n\t\t{\n\t\t\tif(args.count < 3) return -3;\n\t\t\tNSString* appPath = args[1];\n\t\t\tNSString* newRegistration = args[2];\n\n\t\t\tNSString* trollStoreMark = [[appPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:TS_ACTIVE_MARKER];\n\t\t\tif([[NSFileManager defaultManager] fileExistsAtPath:trollStoreMark])\n\t\t\t{\n\t\t\t\tregisterPath(appPath, NO, [newRegistration isEqualToString:@\"System\"]);\n\t\t\t}\n\t\t}\n\t\telse if ([cmd isEqualToString:@\"transfer-apps\"])\n\t\t{\n\t\t\tbool oneFailed = false;\n\t\t\tfor (NSString *appBundlePath in trollStoreInactiveInstalledAppBundlePaths()) {\n\t\t\t\tNSLog(@\"Transfering %@...\", appBundlePath);\n\n\t\t\t\t// Ldid lacks the entitlement to sign in place\n\t\t\t\t// So copy to /tmp, resign, then replace >.<\n\t\t\t\tNSString *tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];\n\t\t\t\tif (![[NSFileManager defaultManager] createDirectoryAtPath:tmpPath withIntermediateDirectories:YES attributes:nil error:nil]) return -3;\n\n\t\t\t\tNSString *tmpAppPath = [tmpPath stringByAppendingPathComponent:appBundlePath.lastPathComponent];\n\t\t\t\tif (![[NSFileManager defaultManager] copyItemAtPath:appBundlePath toPath:tmpAppPath error:nil]) {\n\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpPath error:nil];\n\t\t\t\t\toneFailed = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tNSLog(@\"Copied %@ to %@\", appBundlePath, tmpAppPath);\n\n\t\t\t\tint signRet = signApp(tmpAppPath);\n\t\t\t\tNSLog(@\"Signing %@ returned %d\", tmpAppPath, signRet);\n\n\t\t\t\tif (signRet == 0 || signRet == 182 || signRet == 184) { // Either 0 or non fatal error codes are fine\n\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:appBundlePath error:nil];\n\t\t\t\t\t[[NSFileManager defaultManager] moveItemAtPath:tmpAppPath toPath:appBundlePath error:nil];\n\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpPath error:nil];\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpPath error:nil];\n\t\t\t\t\toneFailed = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfixPermissionsOfAppBundle(appBundlePath);\n\n\t\t\t\tNSString *containerPath = [appBundlePath stringByDeletingLastPathComponent];\n\t\t\t\tNSString *activeMarkerPath = [containerPath stringByAppendingPathComponent:TS_ACTIVE_MARKER];\n\t\t\t\tNSString *inactiveMarkerPath = [containerPath stringByAppendingPathComponent:TS_INACTIVE_MARKER];\n\n\t\t\t\tNSData* emptyData = [NSData data];\n\t\t\t\t[emptyData writeToFile:activeMarkerPath options:0 error:nil];\n\n\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:inactiveMarkerPath error:nil];\n\n\t\t\t\tregisterPath(appBundlePath, 0, !shouldRegisterAsUserByDefault());\n\n\t\t\t\tNSLog(@\"Transfered %@!\", appBundlePath);\n\t\t\t}\n\t\t\tif (oneFailed) ret = -1;\n\t\t}\n#ifndef TROLLSTORE_LITE\n\t\telse if([cmd isEqualToString:@\"install-trollstore\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\tNSString* tsTar = args.lastObject;\n\t\t\tret = installTrollStore(tsTar);\n\t\t\tNSLog(@\"installed troll store? %d\", ret==0);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"uninstall-trollstore\"])\n\t\t{\n\t\t\tif(![args containsObject:@\"preserve-apps\"])\n\t\t\t{\n\t\t\t\tuninstallAllApps([args containsObject:@\"custom\"]);\n\t\t\t}\n\t\t\tuninstallTrollStore(YES);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"install-ldid\"])\n\t\t{\n\t\t\t//if (@available(iOS 16, *)) {} else {\n\t\t\t\tif(args.count < 3) return -3;\n\t\t\t\tNSString* ldidPath = args[1];\n\t\t\t\tNSString* ldidVersion = args[2];\n\t\t\t\tinstallLdid(ldidPath, ldidVersion);\n\t\t\t//}\n\t\t}\n\t\telse if([cmd isEqualToString:@\"install-persistence-helper\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\tNSString* systemAppId = args[1];\n\t\t\tNSString* persistenceHelperBinary;\n\t\t\tNSString* rootHelperBinary;\n\t\t\tif (args.count == 4) {\n\t\t\t\tpersistenceHelperBinary = args[2];\n\t\t\t\trootHelperBinary = args[3];\n\t\t\t}\n\n\t\t\tinstallPersistenceHelper(systemAppId, persistenceHelperBinary, rootHelperBinary);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"uninstall-persistence-helper\"])\n\t\t{\n\t\t\tuninstallPersistenceHelper();\n\t\t}\n\t\telse if([cmd isEqualToString:@\"register-user-persistence-helper\"])\n\t\t{\n\t\t\tif(args.count < 2) return -3;\n\t\t\tNSString* userAppId = args.lastObject;\n\t\t\tregisterUserPersistenceHelper(userAppId);\n\t\t}\n\t\telse if([cmd isEqualToString:@\"check-dev-mode\"])\n\t\t{\n\t\t\t// switch the result, so 0 is enabled, and 1 is disabled/error\n\t\t\tret = !checkDeveloperMode();\n\t\t}\n\t\telse if([cmd isEqualToString:@\"arm-dev-mode\"])\n\t\t{\n\t\t\t// assumes that checkDeveloperMode() has already been called\n\t\t\tret = !armDeveloperMode(NULL);\n\t\t}\n#endif\n\n\t\tNSLog(@\"trollstorehelper returning %d\", ret);\n\t\treturn ret;\n\t}\n}\n"
  },
  {
    "path": "RootHelper/uicache.h",
    "content": "extern bool registerPath(NSString *path, BOOL unregister, BOOL forceSystem);"
  },
  {
    "path": "RootHelper/uicache.m",
    "content": "@import Foundation;\n@import CoreServices;\n#import \"CoreServices.h\"\n#import <objc/runtime.h>\n#import \"dlfcn.h\"\n#import <TSUtil.h>\n#import <version.h>\n\n// uicache on steroids\n\nextern NSSet<NSString*>* immutableAppBundleIdentifiers(void);\nextern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString* binaryPath);\n\nNSDictionary *constructGroupsContainersForEntitlements(NSDictionary *entitlements, BOOL systemGroups) {\n\tif (!entitlements) return nil;\n\n\tNSString *entitlementForGroups;\n\tClass mcmClass;\n\tif (systemGroups) {\n\t\tentitlementForGroups = @\"com.apple.security.system-groups\";\n\t\tmcmClass = [MCMSystemDataContainer class];\n\t}\n\telse {\n\t\tentitlementForGroups = @\"com.apple.security.application-groups\";\n\t\tmcmClass = [MCMSharedDataContainer class];\n\t}\n\n\tNSArray *groupIDs = entitlements[entitlementForGroups];\n\tif (groupIDs && [groupIDs isKindOfClass:[NSArray class]]) {\n\t\tNSMutableDictionary *groupContainers = [NSMutableDictionary new];\n\n\t\tfor (NSString *groupID in groupIDs) {\n\t\t\tMCMContainer *container = [mcmClass containerWithIdentifier:groupID createIfNecessary:YES existed:nil error:nil];\n\t\t\tif (container.url) {\n\t\t\t\tgroupContainers[groupID] = container.url.path;\n\t\t\t}\n\t\t}\n\n\t\treturn groupContainers.copy;\n\t}\n\n\treturn nil;\n}\n\nBOOL constructContainerizationForEntitlements(NSDictionary *entitlements, NSString **customContainerOut) {\n\tNSNumber *noContainer = entitlements[@\"com.apple.private.security.no-container\"];\n\tif (noContainer && [noContainer isKindOfClass:[NSNumber class]]) {\n\t\tif (noContainer.boolValue) {\n\t\t\treturn NO;\n\t\t}\n\t}\n\n\tNSObject *containerRequired = entitlements[@\"com.apple.private.security.container-required\"];\n\tif (containerRequired && [containerRequired isKindOfClass:[NSNumber class]]) {\n\t\tif (!((NSNumber *)containerRequired).boolValue) {\n\t\t\treturn NO;\n\t\t}\n\t}\n\telse if (containerRequired && [containerRequired isKindOfClass:[NSString class]]) {\n\t\t*customContainerOut = (NSString *)containerRequired;\n\t}\n\n\treturn YES;\n}\n\nNSString *constructTeamIdentifierForEntitlements(NSDictionary *entitlements) {\n\tNSString *teamIdentifier = entitlements[@\"com.apple.developer.team-identifier\"];\n\tif (teamIdentifier && [teamIdentifier isKindOfClass:[NSString class]]) {\n\t\treturn teamIdentifier;\n\t}\n\treturn nil;\n}\n\nNSDictionary *constructEnvironmentVariablesForContainerPath(NSString *containerPath, BOOL isContainerized) {\n\tNSString *homeDir = isContainerized ? containerPath : @\"/var/mobile\";\n\tNSString *tmpDir = isContainerized ? [containerPath stringByAppendingPathComponent:@\"tmp\"] : @\"/var/tmp\";\n\treturn @{\n\t\t@\"CFFIXED_USER_HOME\" : homeDir,\n\t\t@\"HOME\" : homeDir,\n\t\t@\"TMPDIR\" : tmpDir\n\t};\n}\n\nbool registerPath(NSString *path, BOOL unregister, BOOL forceSystem) {\n\tif (!path) return false;\n\n\tLSApplicationWorkspace *workspace = [LSApplicationWorkspace defaultWorkspace];\n\tif (unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path]) {\n\t\tLSApplicationProxy *app = [LSApplicationProxy applicationProxyForIdentifier:path];\n\t\tif (app.bundleURL) {\n\t\t\tpath = [app bundleURL].path;\n\t\t}\n\t}\n\n\tpath = path.stringByResolvingSymlinksInPath.stringByStandardizingPath;\n\n\tNSDictionary *appInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@\"Info.plist\"]];\n\tNSString *appBundleID = [appInfoPlist objectForKey:@\"CFBundleIdentifier\"];\n\n\tif([immutableAppBundleIdentifiers() containsObject:appBundleID.lowercaseString]) return false;\n\n\tif (appBundleID && !unregister) {\n\t\tNSString *appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@\"CFBundleExecutable\"]];\n\t\tNSDictionary *entitlements = dumpEntitlementsFromBinaryAtPath(appExecutablePath);\n\n\t\tNSString *appDataContainerID = appBundleID;\n\t\tBOOL appContainerized = constructContainerizationForEntitlements(entitlements, &appDataContainerID);\n\n\t\tMCMContainer *appDataContainer = [NSClassFromString(@\"MCMAppDataContainer\") containerWithIdentifier:appDataContainerID createIfNecessary:YES existed:nil error:nil];\n\t\tNSString *containerPath = [appDataContainer url].path;\n\n\t\tBOOL isRemovableSystemApp = [[NSFileManager defaultManager] fileExistsAtPath:[@\"/System/Library/AppSignatures\" stringByAppendingPathComponent:appBundleID]];\n\t\tBOOL registerAsUser = [path hasPrefix:@\"/var/containers\"] && !isRemovableSystemApp && !forceSystem;\n\n\t\tNSMutableDictionary *dictToRegister = [NSMutableDictionary dictionary];\n\n\t\t// Add entitlements\n\n\t\tif (entitlements) {\n\t\t\tdictToRegister[@\"Entitlements\"] = entitlements;\n\t\t}\n\n\t\t// Misc\n\t\n\t\tdictToRegister[@\"ApplicationType\"] = registerAsUser ? @\"User\" : @\"System\";\n\t\tdictToRegister[@\"CFBundleIdentifier\"] = appBundleID;\n\t\tdictToRegister[@\"CodeInfoIdentifier\"] = appBundleID;\n\t\tdictToRegister[@\"CompatibilityState\"] = @0;\n\t\tdictToRegister[@\"IsContainerized\"] = @(appContainerized);\n\t\tif (containerPath) {\n\t\t\tdictToRegister[@\"Container\"] = containerPath;\n\t\t\tdictToRegister[@\"EnvironmentVariables\"] = constructEnvironmentVariablesForContainerPath(containerPath, appContainerized);\n\t\t}\n\t\tdictToRegister[@\"IsDeletable\"] = @(![appBundleID isEqualToString:@\"com.opa334.TrollStore\"] && kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_15_0);\n\t\tdictToRegister[@\"Path\"] = path;\n\t\t\n\t\tdictToRegister[@\"SignerOrganization\"] = @\"Apple Inc.\";\n\t\tdictToRegister[@\"SignatureVersion\"] = @132352;\n\t\tdictToRegister[@\"SignerIdentity\"] = @\"Apple iPhone OS Application Signing\";\n\t\tdictToRegister[@\"IsAdHocSigned\"] = @YES;\n\t\tdictToRegister[@\"LSInstallType\"] = @1;\n\t\tdictToRegister[@\"HasMIDBasedSINF\"] = @0;\n\t\tdictToRegister[@\"MissingSINF\"] = @0;\n\t\tdictToRegister[@\"FamilyID\"] = @0;\n\t\tdictToRegister[@\"IsOnDemandInstallCapable\"] = @0;\n\n\t\tNSString *teamIdentifier = constructTeamIdentifierForEntitlements(entitlements);\n\t\tif (teamIdentifier) dictToRegister[@\"TeamIdentifier\"] = teamIdentifier;\n\n\t\t// Add group containers\n\n\t\tNSDictionary *appGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO);\n\t\tNSDictionary *systemGroupContainers = constructGroupsContainersForEntitlements(entitlements, YES);\n\t\tNSMutableDictionary *groupContainers = [NSMutableDictionary new];\n\t\t[groupContainers addEntriesFromDictionary:appGroupContainers];\n\t\t[groupContainers addEntriesFromDictionary:systemGroupContainers];\n\t\tif (groupContainers.count) {\n\t\t\tif (appGroupContainers.count) {\n\t\t\t\tdictToRegister[@\"HasAppGroupContainers\"] = @YES;\n\t\t\t}\n\t\t\tif (systemGroupContainers.count) {\n\t\t\t\tdictToRegister[@\"HasSystemGroupContainers\"] = @YES;\n\t\t\t}\n\t\t\tdictToRegister[@\"GroupContainers\"] = groupContainers.copy;\n\t\t}\n\n\t\t// Add plugins\n\n\t\tNSString *pluginsPath = [path stringByAppendingPathComponent:@\"PlugIns\"];\n\t\tNSArray *plugins = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginsPath error:nil];\n\n\t\tNSMutableDictionary *bundlePlugins = [NSMutableDictionary dictionary];\n\t\tfor (NSString *pluginName in plugins) {\n\t\t\tNSString *pluginPath = [pluginsPath stringByAppendingPathComponent:pluginName];\n\n\t\t\tNSDictionary *pluginInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[pluginPath stringByAppendingPathComponent:@\"Info.plist\"]];\n\t\t\tNSString *pluginBundleID = [pluginInfoPlist objectForKey:@\"CFBundleIdentifier\"];\n\n\t\t\tif (!pluginBundleID) continue;\n\t\t\tNSString *pluginExecutablePath = [pluginPath stringByAppendingPathComponent:pluginInfoPlist[@\"CFBundleExecutable\"]];\n\t\t\tNSDictionary *pluginEntitlements = dumpEntitlementsFromBinaryAtPath(pluginExecutablePath);\n\t\t\tNSString *pluginDataContainerID = pluginBundleID;\n\t\t\tBOOL pluginContainerized = constructContainerizationForEntitlements(pluginEntitlements, &pluginDataContainerID);\n\n\t\t\tMCMContainer *pluginContainer = [NSClassFromString(@\"MCMPluginKitPluginDataContainer\") containerWithIdentifier:pluginDataContainerID createIfNecessary:YES existed:nil error:nil];\n\t\t\tNSString *pluginContainerPath = [pluginContainer url].path;\n\n\t\t\tNSMutableDictionary *pluginDict = [NSMutableDictionary dictionary];\n\n\t\t\t// Add entitlements\n\t\t\tif (pluginEntitlements) {\n\t\t\t\tpluginDict[@\"Entitlements\"] = pluginEntitlements;\n\t\t\t}\n\n\t\t\t// Misc\n\n\t\t\tpluginDict[@\"ApplicationType\"] = @\"PluginKitPlugin\";\n\t\t\tpluginDict[@\"CFBundleIdentifier\"] = pluginBundleID;\n\t\t\tpluginDict[@\"CodeInfoIdentifier\"] = pluginBundleID;\n\t\t\tpluginDict[@\"CompatibilityState\"] = @0;\n\t\t\t\n\t\t\tpluginDict[@\"IsContainerized\"] = @(pluginContainerized);\n\t\t\tif (pluginContainerPath) {\n\t\t\t\tpluginDict[@\"Container\"] = pluginContainerPath;\n\t\t\t\tpluginDict[@\"EnvironmentVariables\"] = constructEnvironmentVariablesForContainerPath(pluginContainerPath, pluginContainerized);\n\t\t\t}\n\t\t\tpluginDict[@\"Path\"] = pluginPath;\n\t\t\tpluginDict[@\"PluginOwnerBundleID\"] = appBundleID;\n\t\t\tpluginDict[@\"SignerOrganization\"] = @\"Apple Inc.\";\n\t\t\tpluginDict[@\"SignatureVersion\"] = @132352;\n\t\t\tpluginDict[@\"SignerIdentity\"] = @\"Apple iPhone OS Application Signing\";\n\n\t\t\tNSString *pluginTeamIdentifier = constructTeamIdentifierForEntitlements(pluginEntitlements);\n\t\t\tif (pluginTeamIdentifier) pluginDict[@\"TeamIdentifier\"] = pluginTeamIdentifier;\n\n\t\t\t// Add plugin group containers\n\n\t\t\tNSDictionary *pluginAppGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO);\n\t\t\tNSDictionary *pluginSystemGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, YES);\n\t\t\tNSMutableDictionary *pluginGroupContainers = [NSMutableDictionary new];\n\t\t\t[pluginGroupContainers addEntriesFromDictionary:pluginAppGroupContainers];\n\t\t\t[pluginGroupContainers addEntriesFromDictionary:pluginSystemGroupContainers];\n\t\t\tif (pluginGroupContainers.count) {\n\t\t\t\tif (pluginAppGroupContainers.count) {\n\t\t\t\t\tpluginDict[@\"HasAppGroupContainers\"] = @YES;\n\t\t\t\t}\n\t\t\t\tif (pluginSystemGroupContainers.count) {\n\t\t\t\t\tpluginDict[@\"HasSystemGroupContainers\"] = @YES;\n\t\t\t\t}\n\t\t\t\tpluginDict[@\"GroupContainers\"] = pluginGroupContainers.copy;\n\t\t\t}\n\n\t\t\t[bundlePlugins setObject:pluginDict forKey:pluginBundleID];\n\t\t}\n\t\t[dictToRegister setObject:bundlePlugins forKey:@\"_LSBundlePlugins\"];\n\n\t\tif (![workspace registerApplicationDictionary:dictToRegister]) {\n\t\t\tNSLog(@\"Error: Unable to register %@\", path);\n\t\t\tNSLog(@\"Used dictionary: {\");\n\t\t\t[dictToRegister enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSObject *obj, BOOL *stop) {\n\t\t\t\tNSLog(@\"%@ = %@\", key, obj);\n\t\t\t}];\n\t\t\tNSLog(@\"}\");\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tNSURL *url = [NSURL fileURLWithPath:path];\n\t\tif (![workspace unregisterApplication:url]) {\n\t\t\tNSLog(@\"Error: Unable to register %@\", path);\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "RootHelper/unarchive.h",
    "content": "@import Foundation;\n\nextern int extract(NSString* fileToExtract, NSString* extractionPath);"
  },
  {
    "path": "RootHelper/unarchive.m",
    "content": "#import \"unarchive.h\"\n\n#include <archive.h>\n#include <archive_entry.h>\n\nstatic int\ncopy_data(struct archive *ar, struct archive *aw)\n{\n  int r;\n  const void *buff;\n  size_t size;\n  la_int64_t offset;\n\n  for (;;) {\n    r = archive_read_data_block(ar, &buff, &size, &offset);\n    if (r == ARCHIVE_EOF)\n      return (ARCHIVE_OK);\n    if (r < ARCHIVE_OK)\n      return (r);\n    r = archive_write_data_block(aw, buff, size, offset);\n    if (r < ARCHIVE_OK) {\n      fprintf(stderr, \"%s\\n\", archive_error_string(aw));\n      return (r);\n    }\n  }\n}\n\nint extract(NSString* fileToExtract, NSString* extractionPath)\n{\n    struct archive *a;\n    struct archive *ext;\n    struct archive_entry *entry;\n    int flags;\n    int r;\n\n    /* Select which attributes we want to restore. */\n    flags = ARCHIVE_EXTRACT_TIME;\n    flags |= ARCHIVE_EXTRACT_PERM;\n    flags |= ARCHIVE_EXTRACT_ACL;\n    flags |= ARCHIVE_EXTRACT_FFLAGS;\n\n    a = archive_read_new();\n    archive_read_support_format_all(a);\n    archive_read_support_filter_all(a);\n    ext = archive_write_disk_new();\n    archive_write_disk_set_options(ext, flags);\n    archive_write_disk_set_standard_lookup(ext);\n    if ((r = archive_read_open_filename(a, fileToExtract.fileSystemRepresentation, 10240)))\n        return 1;\n    for (;;)\n    {\n        r = archive_read_next_header(a, &entry);\n        if (r == ARCHIVE_EOF)\n            break;\n        if (r < ARCHIVE_OK)\n            fprintf(stderr, \"%s\\n\", archive_error_string(a));\n        if (r < ARCHIVE_WARN)\n            return 1;\n        \n        NSString* currentFile = [NSString stringWithUTF8String:archive_entry_pathname(entry)];\n        NSString* fullOutputPath = [extractionPath stringByAppendingPathComponent:currentFile];\n        //printf(\"extracting %@ to %@\\n\", currentFile, fullOutputPath);\n        archive_entry_set_pathname(entry, fullOutputPath.fileSystemRepresentation);\n        \n        r = archive_write_header(ext, entry);\n        if (r < ARCHIVE_OK)\n            fprintf(stderr, \"%s\\n\", archive_error_string(ext));\n        else if (archive_entry_size(entry) > 0) {\n            r = copy_data(a, ext);\n            if (r < ARCHIVE_OK)\n                fprintf(stderr, \"%s\\n\", archive_error_string(ext));\n            if (r < ARCHIVE_WARN)\n                return 1;\n        }\n        r = archive_write_finish_entry(ext);\n        if (r < ARCHIVE_OK)\n            fprintf(stderr, \"%s\\n\", archive_error_string(ext));\n        if (r < ARCHIVE_WARN)\n            return 1;\n    }\n    archive_read_close(a);\n    archive_read_free(a);\n    archive_write_close(ext);\n    archive_write_free(ext);\n    \n    return 0;\n}\n"
  },
  {
    "path": "Shared/CoreServices.h",
    "content": "extern NSString *LSInstallTypeKey;\n\n@interface LSBundleProxy\n@property (nonatomic,readonly) NSString * bundleIdentifier;\n@property (nonatomic) NSURL* dataContainerURL;\n@property (nonatomic,readonly) NSURL* bundleContainerURL;\n-(NSString*)localizedName;\n@end\n\n@interface LSApplicationProxy : LSBundleProxy\n+ (instancetype)applicationProxyForIdentifier:(NSString*)identifier;\n+ (instancetype)applicationProxyForBundleURL:(NSURL*)bundleURL;\n@property NSURL* bundleURL;\n@property NSString* bundleType;\n@property NSString* canonicalExecutablePath;\n@property (nonatomic,readonly) NSDictionary* groupContainerURLs;\n@property (nonatomic,readonly) NSArray* plugInKitPlugins;\n@property (getter=isInstalled,nonatomic,readonly) BOOL installed; \n@property (getter=isPlaceholder,nonatomic,readonly) BOOL placeholder; \n@property (getter=isRestricted,nonatomic,readonly) BOOL restricted;\n@property (nonatomic,readonly) NSSet* claimedURLSchemes;\n@property (nonatomic,readonly) NSString* applicationType;\n@end\n\n@interface LSApplicationWorkspace : NSObject\n+ (instancetype)defaultWorkspace;\n- (BOOL)registerApplicationDictionary:(NSDictionary*)dict;\n- (BOOL)unregisterApplication:(id)arg1;\n- (BOOL)_LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)arg1 internal:(BOOL)arg2 user:(BOOL)arg3;\n- (BOOL)openApplicationWithBundleID:(NSString *)arg1 ;\n- (void)enumerateApplicationsOfType:(NSUInteger)type block:(void (^)(LSApplicationProxy*))block;\n- (BOOL)installApplication:(NSURL*)appPackageURL withOptions:(NSDictionary*)options error:(NSError**)error;\n- (BOOL)uninstallApplication:(NSString*)appId withOptions:(NSDictionary*)options;\n- (void)addObserver:(id)arg1;\n- (void)removeObserver:(id)arg1;\n@end\n\n@protocol LSApplicationWorkspaceObserverProtocol <NSObject>\n@optional\n- (void)applicationsDidInstall:(NSArray <LSApplicationProxy *>*)apps;\n- (void)applicationsDidUninstall:(NSArray <LSApplicationProxy *>*)apps;\n@end\n\n@interface LSEnumerator : NSEnumerator\n@property (nonatomic,copy) NSPredicate * predicate;\n+ (instancetype)enumeratorForApplicationProxiesWithOptions:(NSUInteger)options;\n@end\n\n@interface LSPlugInKitProxy : LSBundleProxy\n@property (nonatomic,readonly) NSString* pluginIdentifier;\n@property (nonatomic,readonly) NSDictionary * pluginKitDictionary;\n+ (instancetype)pluginKitProxyForIdentifier:(NSString*)arg1;\n@end\n\n@interface MCMContainer : NSObject\n+ (id)containerWithIdentifier:(id)arg1 createIfNecessary:(BOOL)arg2 existed:(BOOL*)arg3 error:(id*)arg4;\n@property (nonatomic,readonly) NSURL * url;\n@end\n\n@interface MCMDataContainer : MCMContainer\n@end\n\n@interface MCMAppDataContainer : MCMDataContainer\n@end\n\n@interface MCMAppContainer : MCMContainer\n@end\n\n@interface MCMPluginKitPluginDataContainer : MCMDataContainer\n@end\n\n@interface MCMSystemDataContainer : MCMContainer\n@end\n\n@interface MCMSharedDataContainer : MCMContainer\n@end"
  },
  {
    "path": "Shared/TSListControllerShared.h",
    "content": "#import <UIKit/UIKit.h>\n#import <Preferences/PSListController.h>\n#import <Preferences/PSSpecifier.h>\n\n@interface TSListControllerShared : PSListController\n- (BOOL)isTrollStore;\n- (NSString*)getTrollStoreVersion;\n- (void)downloadTrollStoreAndRun:(void (^)(NSString* localTrollStoreTarPath))doHandler;\n- (void)installTrollStorePressed;\n- (void)updateTrollStorePressed;\n- (void)rebuildIconCachePressed;\n- (void)refreshAppRegistrationsPressed;\n- (void)uninstallPersistenceHelperPressed;\n- (void)handleUninstallation;\n- (NSMutableArray*)argsForUninstallingTrollStore;\n- (void)uninstallTrollStorePressed;\n@end"
  },
  {
    "path": "Shared/TSListControllerShared.m",
    "content": "#import \"TSListControllerShared.h\"\n#import \"TSUtil.h\"\n#import \"TSPresentationDelegate.h\"\n\n@implementation TSListControllerShared\n\n- (BOOL)isTrollStore\n{\n\treturn YES;\n}\n\n- (NSString*)getTrollStoreVersion\n{\n\tif([self isTrollStore])\n\t{\n\t\treturn [NSBundle.mainBundle objectForInfoDictionaryKey:@\"CFBundleVersion\"];\n\t}\n\telse\n\t{\n\t\tNSString* trollStorePath = trollStoreAppPath();\n\t\tif(!trollStorePath) return nil;\n\n\t\tNSBundle* trollStoreBundle = [NSBundle bundleWithPath:trollStorePath];\n\t\treturn [trollStoreBundle objectForInfoDictionaryKey:@\"CFBundleVersion\"];\n\t}\n}\n\n- (void)downloadTrollStoreAndRun:(void (^)(NSString* localTrollStoreTarPath))doHandler\n{\n\tNSURL* trollStoreURL = [NSURL URLWithString:@\"https://github.com/opa334/TrollStore/releases/latest/download/TrollStore.tar\"];\n\tNSURLRequest* trollStoreRequest = [NSURLRequest requestWithURL:trollStoreURL];\n\n\tNSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:trollStoreRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)\n\t{\n\t\tif(error)\n\t\t{\n\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@\"Error\" message:[NSString stringWithFormat:@\"Error downloading TrollStore: %@\", error] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t{\n\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:^\n\t\t\t\t{\n\t\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t\t\t}];\n\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\tNSString* tarTmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@\"TrollStore.tar\"];\n\t\t\t[[NSFileManager defaultManager] removeItemAtPath:tarTmpPath error:nil];\n\t\t\t[[NSFileManager defaultManager] copyItemAtPath:location.path toPath:tarTmpPath error:nil];\n\n\t\t\tdoHandler(tarTmpPath);\n\t\t}\n\t}];\n\n\t[downloadTask resume];\n}\n\n- (void)_installTrollStoreComingFromUpdateFlow:(BOOL)update\n{\n\tif(update)\n\t{\n\t\t[TSPresentationDelegate startActivity:@\"Updating TrollStore\"];\n\t}\n\telse\n\t{\n\t\t[TSPresentationDelegate startActivity:@\"Installing TrollStore\"];\n\t}\n\n\t[self downloadTrollStoreAndRun:^(NSString* tmpTarPath)\n\t{\n\t\tint ret = spawnRoot(rootHelperPath(), @[@\"install-trollstore\", tmpTarPath], nil, nil);\n\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpTarPath error:nil];\n\n\t\tif(ret == 0)\n\t\t{\n\t\t\trespring();\n\n\t\t\tif([self isTrollStore])\n\t\t\t{\n\t\t\t\texit(0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t\t{\n\t\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:^\n\t\t\t\t\t{\n\t\t\t\t\t\t[self reloadSpecifiers];\n\t\t\t\t\t}];\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t{\n\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:^\n\t\t\t\t{\n\t\t\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@\"Error\" message:[NSString stringWithFormat:@\"Error installing TrollStore: trollstorehelper returned %d\", ret] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t\t[errorAlert addAction:closeAction];\n\t\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t\t\t}];\n\t\t\t});\n\t\t}\n\t}];\n}\n\n- (void)installTrollStorePressed\n{\n\t[self _installTrollStoreComingFromUpdateFlow:NO];\n}\n\n- (void)updateTrollStorePressed\n{\n\t[self _installTrollStoreComingFromUpdateFlow:YES];\n}\n\n- (void)rebuildIconCachePressed\n{\n\t[TSPresentationDelegate startActivity:@\"Rebuilding Icon Cache\"];\n\n\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^\n\t{\n\t\tspawnRoot(rootHelperPath(), @[@\"refresh-all\"], nil, nil);\n\n\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t{\n\t\t\t[TSPresentationDelegate stopActivityWithCompletion:nil];\n\t\t});\n\t});\n}\n\n- (void)refreshAppRegistrationsPressed\n{\n\t[TSPresentationDelegate startActivity:@\"Refreshing\"];\n\n\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^\n\t{\n\t\tspawnRoot(rootHelperPath(), @[@\"refresh\"], nil, nil);\n\t\trespring();\n\n\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t{\n\t\t\t[TSPresentationDelegate stopActivityWithCompletion:nil];\n\t\t});\n\t});\n}\n\n- (void)uninstallPersistenceHelperPressed\n{\n\tif([self isTrollStore])\n\t{\n\t\tspawnRoot(rootHelperPath(), @[@\"uninstall-persistence-helper\"], nil, nil);\n\t\t[self reloadSpecifiers];\n\t}\n\telse\n\t{\n\t\tUIAlertController* uninstallWarningAlert = [UIAlertController alertControllerWithTitle:@\"Warning\" message:@\"Uninstalling the persistence helper will revert this app back to it's original state, you will however no longer be able to persistently refresh the TrollStore app registrations. Continue?\" preferredStyle:UIAlertControllerStyleAlert];\n\t\n\t\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\t\t[uninstallWarningAlert addAction:cancelAction];\n\n\t\tUIAlertAction* continueAction = [UIAlertAction actionWithTitle:@\"Continue\" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)\n\t\t{\n\t\t\tspawnRoot(rootHelperPath(), @[@\"uninstall-persistence-helper\"], nil, nil);\n\t\t\texit(0);\n\t\t}];\n\t\t[uninstallWarningAlert addAction:continueAction];\n\n\t\t[TSPresentationDelegate presentViewController:uninstallWarningAlert animated:YES completion:nil];\n\t}\n}\n\n- (void)handleUninstallation\n{\n\tif([self isTrollStore])\n\t{\n\t\texit(0);\n\t}\n\telse\n\t{\n\t\t[self reloadSpecifiers];\n\t}\n}\n\n- (NSMutableArray*)argsForUninstallingTrollStore\n{\n\treturn @[@\"uninstall-trollstore\"].mutableCopy;\n}\n\n- (void)uninstallTrollStorePressed\n{\n\tUIAlertController* uninstallAlert = [UIAlertController alertControllerWithTitle:@\"Uninstall\" message:@\"You are about to uninstall TrollStore, do you want to preserve the apps installed by it?\" preferredStyle:UIAlertControllerStyleAlert];\n\t\n\tUIAlertAction* uninstallAllAction = [UIAlertAction actionWithTitle:@\"Uninstall TrollStore, Uninstall Apps\" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)\n\t{\n\t\tNSMutableArray* args = [self argsForUninstallingTrollStore];\n\t\tspawnRoot(rootHelperPath(), args, nil, nil);\n\t\t[self handleUninstallation];\n\t}];\n\t[uninstallAlert addAction:uninstallAllAction];\n\n\tUIAlertAction* preserveAppsAction = [UIAlertAction actionWithTitle:@\"Uninstall TrollStore, Preserve Apps\" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)\n\t{\n\t\tNSMutableArray* args = [self argsForUninstallingTrollStore];\n\t\t[args addObject:@\"preserve-apps\"];\n\t\tspawnRoot(rootHelperPath(), args, nil, nil);\n\t\t[self handleUninstallation];\n\t}];\n\t[uninstallAlert addAction:preserveAppsAction];\n\n\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\t[uninstallAlert addAction:cancelAction];\n\n\t[TSPresentationDelegate presentViewController:uninstallAlert animated:YES completion:nil];\n}\n\n@end"
  },
  {
    "path": "Shared/TSPresentationDelegate.h",
    "content": "#import <UIKit/UIKit.h>\n\n@interface TSPresentationDelegate : NSObject\n@property (class) UIViewController* presentationViewController;\n@property (class) UIAlertController* activityController;\n+ (void)startActivity:(NSString*)activity withCancelHandler:(void (^)(void))cancelHandler;\n+ (void)startActivity:(NSString*)activity;\n+ (void)stopActivityWithCompletion:(void (^)(void))completion;\n+ (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion;\n@end"
  },
  {
    "path": "Shared/TSPresentationDelegate.m",
    "content": "#import \"TSPresentationDelegate.h\"\n\n@implementation TSPresentationDelegate\n\nstatic UIViewController* g_presentationViewController;\nstatic UIAlertController* g_activityController;\n\n+ (UIViewController*)presentationViewController\n{\n\treturn g_presentationViewController;\n}\n\n+ (void)setPresentationViewController:(UIViewController*)vc\n{\n\tg_presentationViewController = vc;\n}\n\n+ (UIAlertController*)activityController\n{\n\treturn g_activityController;\n}\n\n+ (void)setActivityController:(UIAlertController*)ac\n{\n\tg_activityController = ac;\n}\n\n+ (void)startActivity:(NSString*)activity withCancelHandler:(void (^)(void))cancelHandler\n{\n\tif(self.activityController)\n\t{\n\t\tself.activityController.title = activity;\n\t}\n\telse\n\t{\n\t\tself.activityController = [UIAlertController alertControllerWithTitle:activity message:@\"\" preferredStyle:UIAlertControllerStyleAlert];\n\t\tUIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(5,5,50,50)];\n\t\tactivityIndicator.hidesWhenStopped = YES;\n\t\tactivityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium;\n\t\t[activityIndicator startAnimating];\n\t\t[self.activityController.view addSubview:activityIndicator];\n\n\t\tif(cancelHandler)\n\t\t{\n\t\t\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)\n\t\t\t{\n\t\t\t\tself.activityController = nil;\n\t\t\t\tcancelHandler();\n\t\t\t}];\n\t\t\t[self.activityController addAction:cancelAction];\n\t\t}\n\n\t\t[self presentViewController:self.activityController animated:YES completion:nil];\n\t}\n}\n\n+ (void)startActivity:(NSString*)activity\n{\n\t[self startActivity:activity withCancelHandler:nil];\n}\n\n+ (void)stopActivityWithCompletion:(void (^)(void))completionBlock\n{\n\tif(!self.activityController) return;\n\n\t[self.activityController dismissViewControllerAnimated:YES completion:^\n\t{\n\t\tself.activityController = nil;\n\t\tif(completionBlock) completionBlock();\n\t}];\n}\n\n+ (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completionBlock\n{\n\t[self.presentationViewController presentViewController:viewControllerToPresent animated:flag completion:completionBlock];\n}\n\n@end"
  },
  {
    "path": "Shared/TSUtil.h",
    "content": "@import Foundation;\n#import \"CoreServices.h\"\n\n#define TrollStoreErrorDomain @\"TrollStoreErrorDomain\"\n\n#define TS_MARKER @\"_TrollStore\"\n#define TS_LITE_MARKER @\"_TrollStoreLite\"\n#define TS_NAME @\"TrollStore\"\n#define TS_LITE_NAME @\"Trollstore Lite\"\n\n#ifdef TROLLSTORE_LITE\n#define TS_ACTIVE_MARKER TS_LITE_MARKER\n#define TS_INACTIVE_MARKER TS_MARKER\n#define APP_ID @\"com.opa334.TrollStoreLite\"\n#define APP_NAME TS_LITE_NAME\n#define OTHER_APP_NAME TS_NAME\n#else\n#define TS_ACTIVE_MARKER TS_MARKER\n#define TS_INACTIVE_MARKER TS_LITE_MARKER\n#define APP_ID @\"com.opa334.TrollStore\"\n#define APP_NAME TS_NAME\n#define OTHER_APP_NAME TS_LITE_NAME\n#endif\n\nextern void chineseWifiFixup(void);\nextern NSString *getExecutablePath(void);\nextern BOOL shouldRegisterAsUserByDefault(void);\nextern NSString* rootHelperPath(void);\nextern NSString* getNSStringFromFile(int fd);\nextern void printMultilineNSString(NSString* stringToPrint);\nextern int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr);\nextern void killall(NSString* processName, BOOL softly);\nextern void respring(void);\nextern void fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion));\nextern void fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion));\n\nextern NSArray* trollStoreInstalledAppBundlePaths(void);\nextern NSArray* trollStoreInactiveInstalledAppBundlePaths(void);\nextern NSArray* trollStoreInstalledAppContainerPaths(void);\nextern NSString* trollStorePath(void);\nextern NSString* trollStoreAppPath(void);\n\nextern BOOL isRemovableSystemApp(NSString* appId);\n\n#import <UIKit/UIAlertController.h>\n\n@interface UIAlertController (Private)\n@property (setter=_setAttributedTitle:,getter=_attributedTitle,nonatomic,copy) NSAttributedString* attributedTitle;\n@property (setter=_setAttributedMessage:,getter=_attributedMessage,nonatomic,copy) NSAttributedString* attributedMessage;\n@property (nonatomic,retain) UIImage* image;\n@end\n\ntypedef enum\n{\n\tPERSISTENCE_HELPER_TYPE_USER = 1 << 0,\n\tPERSISTENCE_HELPER_TYPE_SYSTEM = 1 << 1,\n\tPERSISTENCE_HELPER_TYPE_ALL = PERSISTENCE_HELPER_TYPE_USER | PERSISTENCE_HELPER_TYPE_SYSTEM\n} PERSISTENCE_HELPER_TYPE;\n\n// EXPLOIT_TYPE is defined as a bitmask as some devices are vulnerable to multiple exploits\n//\n// An app that has had one of these exploits applied ahead of time can declare which exploit\n// was used via the TSPreAppliedExploitType Info.plist key. The corresponding value should be\n// (number of bits to left-shift + 1).\ntypedef enum\n{\n\t// CVE-2022-26766\n    // TSPreAppliedExploitType = 1\n\tEXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1 = 1 << 0,\n\n\t// CVE-2023-41991\n    // TSPreAppliedExploitType = 2\n\tEXPLOIT_TYPE_CMS_SIGNERINFO_V1 = 1 << 1\n} EXPLOIT_TYPE;\n\nextern LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes);\n\ntypedef struct __SecCode const *SecStaticCodeRef;\n\ntypedef CF_OPTIONS(uint32_t, SecCSFlags) {\n\tkSecCSDefaultFlags = 0\n};\n#define kSecCSRequirementInformation 1 << 2\n#define kSecCSSigningInformation 1 << 1\n\nOSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flags, CFDictionaryRef attributes, SecStaticCodeRef *staticCode);\nOSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, CFDictionaryRef *information);\nCFDataRef SecCertificateCopyExtensionValue(SecCertificateRef certificate, CFTypeRef extensionOID, bool *isCritical);\nvoid SecPolicySetOptionsValue(SecPolicyRef policy, CFStringRef key, CFTypeRef value);\n\nextern CFStringRef kSecCodeInfoEntitlementsDict;\nextern CFStringRef kSecCodeInfoCertificates;\nextern CFStringRef kSecPolicyAppleiPhoneApplicationSigning;\nextern CFStringRef kSecPolicyAppleiPhoneProfileApplicationSigning;\nextern CFStringRef kSecPolicyLeafMarkerOid;\n\nextern SecStaticCodeRef getStaticCodeRef(NSString *binaryPath);\nextern NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef);\nextern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath);\nextern NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData);\n\nextern EXPLOIT_TYPE getDeclaredExploitTypeFromInfoDictionary(NSDictionary *infoDict);\nextern bool isPlatformVulnerableToExploitType(EXPLOIT_TYPE exploitType);\n"
  },
  {
    "path": "Shared/TSUtil.m",
    "content": "#import \"TSUtil.h\"\n\n#import <Foundation/Foundation.h>\n#import <spawn.h>\n#import <sys/sysctl.h>\n#import <mach-o/dyld.h>\n#import <libroot.h>\n\nstatic EXPLOIT_TYPE gPlatformVulnerabilities;\n\nvoid* _CTServerConnectionCreate(CFAllocatorRef, void *, void *);\nint64_t _CTServerConnectionSetCellularUsagePolicy(CFTypeRef* ct, NSString* identifier, NSDictionary* policies);\n\n#define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1\nextern int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t);\nextern int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t);\nextern int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t);\n\nvoid chineseWifiFixup(void)\n{\n\t_CTServerConnectionSetCellularUsagePolicy(\n\t\t_CTServerConnectionCreate(kCFAllocatorDefault, NULL, NULL),\n\t\tNSBundle.mainBundle.bundleIdentifier,\n\t\t@{\n\t\t\t@\"kCTCellularDataUsagePolicy\" : @\"kCTCellularDataUsagePolicyAlwaysAllow\",\n\t\t\t@\"kCTWiFiDataUsagePolicy\" : @\"kCTCellularDataUsagePolicyAlwaysAllow\"\n\t\t}\n\t);\n}\n\nNSString *getExecutablePath(void)\n{\n\tuint32_t len = PATH_MAX;\n\tchar selfPath[len];\n\t_NSGetExecutablePath(selfPath, &len);\n\treturn [NSString stringWithUTF8String:selfPath];\n}\n\n#ifdef TROLLSTORE_LITE\n\nBOOL shouldRegisterAsUserByDefault(void)\n{\n\tif ([[NSFileManager defaultManager] fileExistsAtPath:JBROOT_PATH(@\"/Library/MobileSubstrate/DynamicLibraries/AppSyncUnified-FrontBoard.dylib\")]) {\n\t\treturn YES;\n\t}\n\treturn NO;\n}\n\n#else\n\nBOOL shouldRegisterAsUserByDefault(void)\n{\n\treturn NO;\n}\n\n#endif\n\n#ifdef EMBEDDED_ROOT_HELPER\nNSString* rootHelperPath(void)\n{\n\treturn getExecutablePath();\n}\n#else\nNSString* rootHelperPath(void)\n{\n\treturn [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@\"trollstorehelper\"];\n}\n#endif\n\nint fd_is_valid(int fd)\n{\n\treturn fcntl(fd, F_GETFD) != -1 || errno != EBADF;\n}\n\nNSString* getNSStringFromFile(int fd)\n{\n\tNSMutableString* ms = [NSMutableString new];\n\tssize_t num_read;\n\tchar c;\n\tif(!fd_is_valid(fd)) return @\"\";\n\twhile((num_read = read(fd, &c, sizeof(c))))\n\t{\n\t\t[ms appendString:[NSString stringWithFormat:@\"%c\", c]];\n\t\tif(c == '\\n') break;\n\t}\n\treturn ms.copy;\n}\n\nvoid printMultilineNSString(NSString* stringToPrint)\n{\n\tNSCharacterSet *separator = [NSCharacterSet newlineCharacterSet];\n\tNSArray* lines = [stringToPrint componentsSeparatedByCharactersInSet:separator];\n\tfor(NSString* line in lines)\n\t{\n\t\tNSLog(@\"%@\", line);\n\t}\n}\n\nint spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr)\n{\n\tNSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new];\n\t[argsM insertObject:path atIndex:0];\n\t\n\tNSUInteger argCount = [argsM count];\n\tchar **argsC = (char **)malloc((argCount + 1) * sizeof(char*));\n\n\tfor (NSUInteger i = 0; i < argCount; i++)\n\t{\n\t\targsC[i] = strdup([[argsM objectAtIndex:i] UTF8String]);\n\t}\n\targsC[argCount] = NULL;\n\n\tposix_spawnattr_t attr;\n\tposix_spawnattr_init(&attr);\n\n\tposix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);\n\tposix_spawnattr_set_persona_uid_np(&attr, 0);\n\tposix_spawnattr_set_persona_gid_np(&attr, 0);\n\n\tposix_spawn_file_actions_t action;\n\tposix_spawn_file_actions_init(&action);\n\n\tint outErr[2];\n\tif(stdErr)\n\t{\n\t\tpipe(outErr);\n\t\tposix_spawn_file_actions_adddup2(&action, outErr[1], STDERR_FILENO);\n\t\tposix_spawn_file_actions_addclose(&action, outErr[0]);\n\t}\n\n\tint out[2];\n\tif(stdOut)\n\t{\n\t\tpipe(out);\n\t\tposix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);\n\t\tposix_spawn_file_actions_addclose(&action, out[0]);\n\t}\n\t\n\tpid_t task_pid;\n\tint status = -200;\n\tint spawnError = posix_spawn(&task_pid, [path UTF8String], &action, &attr, (char* const*)argsC, NULL);\n\tposix_spawnattr_destroy(&attr);\n\tfor (NSUInteger i = 0; i < argCount; i++)\n\t{\n\t\tfree(argsC[i]);\n\t}\n\tfree(argsC);\n\t\n\tif(spawnError != 0)\n\t{\n\t\tNSLog(@\"posix_spawn error %d\\n\", spawnError);\n\t\treturn spawnError;\n\t}\n\n\t__block volatile BOOL _isRunning = YES;\n\tNSMutableString* outString = [NSMutableString new];\n\tNSMutableString* errString = [NSMutableString new];\n\tdispatch_semaphore_t sema = 0;\n\tdispatch_queue_t logQueue;\n\tif(stdOut || stdErr)\n\t{\n\t\tlogQueue = dispatch_queue_create(\"com.opa334.TrollStore.LogCollector\", NULL);\n\t\tsema = dispatch_semaphore_create(0);\n\n\t\tint outPipe = out[0];\n\t\tint outErrPipe = outErr[0];\n\n\t\t__block BOOL outEnabled = (BOOL)stdOut;\n\t\t__block BOOL errEnabled = (BOOL)stdErr;\n\t\tdispatch_async(logQueue, ^\n\t\t{\n\t\t\twhile(_isRunning)\n\t\t\t{\n\t\t\t\t@autoreleasepool\n\t\t\t\t{\n\t\t\t\t\tif(outEnabled)\n\t\t\t\t\t{\n\t\t\t\t\t\t[outString appendString:getNSStringFromFile(outPipe)];\n\t\t\t\t\t}\n\t\t\t\t\tif(errEnabled)\n\t\t\t\t\t{\n\t\t\t\t\t\t[errString appendString:getNSStringFromFile(outErrPipe)];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tdispatch_semaphore_signal(sema);\n\t\t});\n\t}\n\n\tdo\n\t{\n\t\tif (waitpid(task_pid, &status, 0) != -1) {\n\t\t\tNSLog(@\"Child status %d\", WEXITSTATUS(status));\n\t\t} else\n\t\t{\n\t\t\tperror(\"waitpid\");\n\t\t\t_isRunning = NO;\n\t\t\treturn -222;\n\t\t}\n\t} while (!WIFEXITED(status) && !WIFSIGNALED(status));\n\n\t_isRunning = NO;\n\tif(stdOut || stdErr)\n\t{\n\t\tif(stdOut)\n\t\t{\n\t\t\tclose(out[1]);\n\t\t}\n\t\tif(stdErr)\n\t\t{\n\t\t\tclose(outErr[1]);\n\t\t}\n\n\t\t// wait for logging queue to finish\n\t\tdispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);\n\n\t\tif(stdOut)\n\t\t{\n\t\t\t*stdOut = outString.copy;\n\t\t}\n\t\tif(stdErr)\n\t\t{\n\t\t\t*stdErr = errString.copy;\n\t\t}\n\t}\n\n\treturn WEXITSTATUS(status);\n}\n\nvoid enumerateProcessesUsingBlock(void (^enumerator)(pid_t pid, NSString* executablePath, BOOL* stop))\n{\n\tstatic int maxArgumentSize = 0;\n\tif (maxArgumentSize == 0) {\n\t\tsize_t size = sizeof(maxArgumentSize);\n\t\tif (sysctl((int[]){ CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) {\n\t\t\tperror(\"sysctl argument size\");\n\t\t\tmaxArgumentSize = 4096; // Default\n\t\t}\n\t}\n\tint mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL};\n\tstruct kinfo_proc *info;\n\tsize_t length;\n\tint count;\n\t\n\tif (sysctl(mib, 3, NULL, &length, NULL, 0) < 0)\n\t\treturn;\n\tif (!(info = malloc(length)))\n\t\treturn;\n\tif (sysctl(mib, 3, info, &length, NULL, 0) < 0) {\n\t\tfree(info);\n\t\treturn;\n\t}\n\tcount = length / sizeof(struct kinfo_proc);\n\tfor (int i = 0; i < count; i++) {\n\t\t@autoreleasepool {\n\t\tpid_t pid = info[i].kp_proc.p_pid;\n\t\tif (pid == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tsize_t size = maxArgumentSize;\n\t\tchar* buffer = (char *)malloc(length);\n\t\tif (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) {\n\t\t\tNSString* executablePath = [NSString stringWithCString:(buffer+sizeof(int)) encoding:NSUTF8StringEncoding];\n\t\t\t\n\t\t\tBOOL stop = NO;\n\t\t\tenumerator(pid, executablePath, &stop);\n\t\t\tif(stop)\n\t\t\t{\n\t\t\t\tfree(buffer);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfree(buffer);\n\t\t}\n\t}\n\tfree(info);\n}\n\nvoid killall(NSString* processName, BOOL softly)\n{\n\tenumerateProcessesUsingBlock(^(pid_t pid, NSString* executablePath, BOOL* stop)\n\t{\n\t\tif([executablePath.lastPathComponent isEqualToString:processName])\n\t\t{\n\t\t\tif(softly)\n\t\t\t{\n\t\t\t\tkill(pid, SIGTERM);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tkill(pid, SIGKILL);\n\t\t\t}\n\t\t}\n\t});\n}\n\nvoid respring(void)\n{\n\tkillall(@\"SpringBoard\", YES);\n\texit(0);\n}\n\nvoid github_fetchLatestVersion(NSString* repo, void (^completionHandler)(NSString* latestVersion))\n{\n\tNSString* urlString = [NSString stringWithFormat:@\"https://api.github.com/repos/%@/releases/latest\", repo];\n\tNSURL* githubLatestAPIURL = [NSURL URLWithString:urlString];\n\n\tNSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithURL:githubLatestAPIURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)\n\t{\n\t\tif(!error)\n\t\t{\n\t\t\tif ([response isKindOfClass:[NSHTTPURLResponse class]])\n\t\t\t{\n\t\t\t\tNSError *jsonError;\n\t\t\t\tNSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];\n\n\t\t\t\tif (!jsonError)\n\t\t\t\t{\n\t\t\t\t\tcompletionHandler(jsonResponse[@\"tag_name\"]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}];\n\n\t[task resume];\n}\n\nvoid fetchLatestTrollStoreVersion(void (^completionHandler)(NSString* latestVersion))\n{\n\tgithub_fetchLatestVersion(@\"opa334/TrollStore\", completionHandler);\n}\n\nvoid fetchLatestLdidVersion(void (^completionHandler)(NSString* latestVersion))\n{\n\tgithub_fetchLatestVersion(@\"opa334/ldid\", completionHandler);\n}\n\nNSArray* trollStoreInstalledAppContainerPathsInternal(NSString *marker)\n{\n\tNSMutableArray* appContainerPaths = [NSMutableArray new];\n\n\tNSString* appContainersPath = @\"/var/containers/Bundle/Application\";\n\n\tNSError* error;\n\tNSArray* containers = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appContainersPath error:&error];\n\tif(error)\n\t{\n\t\tNSLog(@\"error getting app bundles paths %@\", error);\n\t}\n\tif(!containers) return nil;\n\t\n\tfor(NSString* container in containers)\n\t{\n\t\tNSString* containerPath = [appContainersPath stringByAppendingPathComponent:container];\n\t\tBOOL isDirectory = NO;\n\t\tBOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:containerPath isDirectory:&isDirectory];\n\t\tif(exists && isDirectory)\n\t\t{\n\t\t\tNSString* trollStoreMark = [containerPath stringByAppendingPathComponent:marker];\n\t\t\tif([[NSFileManager defaultManager] fileExistsAtPath:trollStoreMark])\n\t\t\t{\n\t\t\t\tNSString* trollStoreApp = [containerPath stringByAppendingPathComponent:@\"TrollStore.app\"];\n\t\t\t\tNSString* trollStoreLiteApp = [containerPath stringByAppendingPathComponent:@\"TrollStoreLite.app\"];\n\t\t\t\tif(![[NSFileManager defaultManager] fileExistsAtPath:trollStoreApp] && ![[NSFileManager defaultManager] fileExistsAtPath:trollStoreLiteApp])\n\t\t\t\t{\n\t\t\t\t\t[appContainerPaths addObject:containerPath];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn appContainerPaths.copy;\n}\n\nNSArray *trollStoreInstalledAppContainerPaths(void)\n{\n\treturn trollStoreInstalledAppContainerPathsInternal(TS_ACTIVE_MARKER);\n}\n\nNSArray* trollStoreInstalledAppBundlePathsInternal(NSString *marker)\n{\n\tNSMutableArray* appPaths = [NSMutableArray new];\n\tfor(NSString* containerPath in trollStoreInstalledAppContainerPathsInternal(marker))\n\t{\n\t\tNSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:containerPath error:nil];\n\t\tif(!items) return nil;\n\t\t\n\t\tfor(NSString* item in items)\n\t\t{\n\t\t\tif([item.pathExtension isEqualToString:@\"app\"])\n\t\t\t{\n\t\t\t\t[appPaths addObject:[containerPath stringByAppendingPathComponent:item]];\n\t\t\t}\n\t\t}\n\t}\n\treturn appPaths.copy;\n}\n\nNSArray *trollStoreInstalledAppBundlePaths(void)\n{\n\treturn trollStoreInstalledAppBundlePathsInternal(TS_ACTIVE_MARKER);\n}\n\nNSArray *trollStoreInactiveInstalledAppBundlePaths(void)\n{\n\treturn trollStoreInstalledAppBundlePathsInternal(TS_INACTIVE_MARKER);\n}\n\nNSString* trollStorePath()\n{\n\tNSError* mcmError;\n\tMCMAppContainer* appContainer = [MCMAppContainer containerWithIdentifier:APP_ID createIfNecessary:NO existed:NULL error:&mcmError];\n\tif(!appContainer) return nil;\n\treturn appContainer.url.path;\n}\n\nNSString* trollStoreAppPath()\n{\n\treturn [trollStorePath() stringByAppendingPathComponent:@\"TrollStore.app\"];\n}\n\nBOOL isRemovableSystemApp(NSString* appId)\n{\n\treturn [[NSFileManager defaultManager] fileExistsAtPath:[@\"/System/Library/AppSignatures\" stringByAppendingPathComponent:appId]];\n}\n\nLSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes)\n{\n\t__block LSApplicationProxy* outProxy;\n\n\tvoid (^searchBlock)(LSApplicationProxy* appProxy) = ^(LSApplicationProxy* appProxy)\n\t{\n\t\tif(appProxy.installed && !appProxy.restricted)\n\t\t{\n\t\t\tif([appProxy.bundleURL.path hasPrefix:@\"/private/var/containers\"])\n\t\t\t{\n\t\t\t\tNSURL* trollStorePersistenceMarkURL = [appProxy.bundleURL URLByAppendingPathComponent:@\".TrollStorePersistenceHelper\"];\n\t\t\t\tif([trollStorePersistenceMarkURL checkResourceIsReachableAndReturnError:nil])\n\t\t\t\t{\n\t\t\t\t\toutProxy = appProxy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tif(allowedTypes & PERSISTENCE_HELPER_TYPE_USER)\n\t{\n\t\t[[LSApplicationWorkspace defaultWorkspace] enumerateApplicationsOfType:0 block:searchBlock];\n\t}\n\tif(allowedTypes & PERSISTENCE_HELPER_TYPE_SYSTEM)\n\t{\n\t\t[[LSApplicationWorkspace defaultWorkspace] enumerateApplicationsOfType:1 block:searchBlock];\n\t}\n\n\treturn outProxy;\n}\n\nSecStaticCodeRef getStaticCodeRef(NSString *binaryPath)\n{\n\tif(binaryPath == nil)\n\t{\n\t\treturn NULL;\n\t}\n\t\n\tCFURLRef binaryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)binaryPath, kCFURLPOSIXPathStyle, false);\n\tif(binaryURL == NULL)\n\t{\n\t\tNSLog(@\"[getStaticCodeRef] failed to get URL to binary %@\", binaryPath);\n\t\treturn NULL;\n\t}\n\t\n\tSecStaticCodeRef codeRef = NULL;\n\tOSStatus result;\n\t\n\tresult = SecStaticCodeCreateWithPathAndAttributes(binaryURL, kSecCSDefaultFlags, NULL, &codeRef);\n\t\n\tCFRelease(binaryURL);\n\t\n\tif(result != errSecSuccess)\n\t{\n\t\tNSLog(@\"[getStaticCodeRef] failed to create static code for binary %@\", binaryPath);\n\t\treturn NULL;\n\t}\n\t\t\n\treturn codeRef;\n}\n\nNSDictionary* dumpEntitlements(SecStaticCodeRef codeRef)\n{\n\tif(codeRef == NULL)\n\t{\n\t\tNSLog(@\"[dumpEntitlements] attempting to dump entitlements without a StaticCodeRef\");\n\t\treturn nil;\n\t}\n\t\n\tCFDictionaryRef signingInfo = NULL;\n\tOSStatus result;\n\t\n\tresult = SecCodeCopySigningInformation(codeRef, kSecCSRequirementInformation, &signingInfo);\n\t\n\tif(result != errSecSuccess)\n\t{\n\t\tNSLog(@\"[dumpEntitlements] failed to copy signing info from static code\");\n\t\treturn nil;\n\t}\n\t\n\tNSDictionary *entitlementsNSDict = nil;\n\t\n\tCFDictionaryRef entitlements = CFDictionaryGetValue(signingInfo, kSecCodeInfoEntitlementsDict);\n\tif(entitlements == NULL)\n\t{\n\t\tNSLog(@\"[dumpEntitlements] no entitlements specified\");\n\t}\n\telse if(CFGetTypeID(entitlements) != CFDictionaryGetTypeID())\n\t{\n\t\tNSLog(@\"[dumpEntitlements] invalid entitlements\");\n\t}\n\telse\n\t{\n\t\tentitlementsNSDict = (__bridge NSDictionary *)(entitlements);\n\t\tNSLog(@\"[dumpEntitlements] dumped %@\", entitlementsNSDict);\n\t}\n\t\n\tCFRelease(signingInfo);\n\treturn entitlementsNSDict;\n}\n\nNSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath)\n{\n\t// This function is intended for one-shot checks. Main-event functions should retain/release their own SecStaticCodeRefs\n\t\n\tif(binaryPath == nil)\n\t{\n\t\treturn nil;\n\t}\n\t\n\tSecStaticCodeRef codeRef = getStaticCodeRef(binaryPath);\n\tif(codeRef == NULL)\n\t{\n\t\treturn nil;\n\t}\n\t\n\tNSDictionary *entitlements = dumpEntitlements(codeRef);\n\tCFRelease(codeRef);\n\n\treturn entitlements;\n}\n\nNSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData)\n{\n\tNSDictionary* entitlements;\n\tNSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID UUID].UUIDString];\n\tNSURL* tmpURL = [NSURL fileURLWithPath:tmpPath];\n\tif([binaryData writeToURL:tmpURL options:NSDataWritingAtomic error:nil])\n\t{\n\t\tentitlements = dumpEntitlementsFromBinaryAtPath(tmpPath);\n\t\t[[NSFileManager defaultManager] removeItemAtURL:tmpURL error:nil];\n\t}\n\treturn entitlements;\n}\n\nEXPLOIT_TYPE getDeclaredExploitTypeFromInfoDictionary(NSDictionary *infoDict)\n{\n    NSObject *tsPreAppliedExploitType = infoDict[@\"TSPreAppliedExploitType\"];\n    if([tsPreAppliedExploitType isKindOfClass:[NSNumber class]])\n    {\n        NSNumber *tsPreAppliedExploitTypeNum = (NSNumber *)tsPreAppliedExploitType;\n        int exploitTypeInt = [tsPreAppliedExploitTypeNum intValue];\n\n        if(exploitTypeInt > 0)\n        {\n            // Convert versions 1, 2, etc... for use with bitmasking\n            return (1 << (exploitTypeInt - 1));\n        }\n        else\n        {\n            NSLog(@\"[getDeclaredExploitTypeFromInfoDictionary] rejecting TSPreAppliedExploitType Info.plist value (%i) which is out of range\", exploitTypeInt);\n        }\n    }\n\n    // Legacy Info.plist flag - now deprecated, but we treat it as a custom root cert if present\n    NSObject *tsBundleIsPreSigned = infoDict[@\"TSBundlePreSigned\"];\n    if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]])\n    {\n        NSNumber *tsBundleIsPreSignedNum = (NSNumber *)tsBundleIsPreSigned;\n        if([tsBundleIsPreSignedNum boolValue] == YES)\n        {\n            return EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1;\n        }\n    }\n\n    // No declarations\n    return 0;\n}\n\nvoid determinePlatformVulnerableExploitTypes(void *context) {\n\tsize_t size = 0;\n\n\t// Get the current build number\n\tint mib[2] = {CTL_KERN, KERN_OSVERSION};\n\n\t// Get size of buffer\n\tsysctl(mib, 2, NULL, &size, NULL, 0);\n\n\t// Get the actual value\n\tchar *os_build = malloc(size);\n\tif(!os_build)\n\t{\n\t\t// malloc failed\n\t\tperror(\"malloc buffer for KERN_OSVERSION\");\n\t\treturn;\n\t}\n\n\tif (sysctl(mib, 2, os_build, &size, NULL, 0) != 0)\n\t{\n\t\t// sysctl failed\n\t\tperror(\"sysctl KERN_OSVERSION\");\n\t\tfree(os_build);\n\t\treturn;\n\t}\n\n\n    if(strncmp(os_build, \"18A5319i\", 8) < 0) {\n        // Below iOS 14.0 beta 2\n        gPlatformVulnerabilities = 0;\n    }\n    else if(strncmp(os_build, \"21A326\", 6) >= 0 && strncmp(os_build, \"21A331\", 6) <= 0)\n    {\n        // iOS 17.0 final\n        gPlatformVulnerabilities = EXPLOIT_TYPE_CMS_SIGNERINFO_V1;\n    }\n    else if(strncmp(os_build, \"21A5248v\", 8) >= 0 && strncmp(os_build, \"21A5326a\", 8) <= 0)\n    {\n        // iOS 17.0 beta 1 - 8\n        gPlatformVulnerabilities = EXPLOIT_TYPE_CMS_SIGNERINFO_V1;\n    }\n    else if(strncmp(os_build, \"19G5027e\", 8) >= 0 && strncmp(os_build, \"19G5063a\", 8) <= 0)\n    {\n        // iOS 15.6 beta 1 - 5\n        gPlatformVulnerabilities = (EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1 | EXPLOIT_TYPE_CMS_SIGNERINFO_V1);\n    }\n    else if(strncmp(os_build, \"19F5070b\", 8) <= 0)\n    {\n        // iOS 14.0 beta 2 - 15.5 beta 4\n        gPlatformVulnerabilities = (EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1 | EXPLOIT_TYPE_CMS_SIGNERINFO_V1);\n    }\n    else if(strncmp(os_build, \"20H18\", 5) <= 0)\n    {\n        // iOS 14.0 - 16.6.1, 16.7 RC (if CUSTOM_ROOT_CERTIFICATE_V1 not supported)\n        gPlatformVulnerabilities = EXPLOIT_TYPE_CMS_SIGNERINFO_V1;\n    }\n\n\tfree(os_build);\n}\n\nbool isPlatformVulnerableToExploitType(EXPLOIT_TYPE exploitType) {\n\t// Find out what we are vulnerable to\n\tstatic dispatch_once_t once;\n\tdispatch_once_f(&once, NULL, determinePlatformVulnerableExploitTypes);\n\n\treturn (exploitType & gPlatformVulnerabilities) != 0;\n}\n"
  },
  {
    "path": "TrollHelper/Makefile",
    "content": "export EMBEDDED_ROOT_HELPER ?= 0\nexport LEGACY_CT_BUG ?= 0\n\nTARGET := iphone:clang:16.5:14.0\nINSTALL_TARGET_PROCESSES = TrollStorePersistenceHelper\n\nifdef CUSTOM_ARCHS\nARCHS = $(CUSTOM_ARCHS)\nelse\nARCHS = arm64\nendif\n\nifneq ($(LEGACY_CT_BUG),1)\nTARGET_CODESIGN = ../Exploits/fastPathSign/fastPathSign\nendif\n\ninclude $(THEOS)/makefiles/common.mk\n\nAPPLICATION_NAME = TrollStorePersistenceHelper\n\nTrollStorePersistenceHelper_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m)\nTrollStorePersistenceHelper_FRAMEWORKS = UIKit CoreGraphics CoreServices CoreTelephony\nTrollStorePersistenceHelper_PRIVATE_FRAMEWORKS = Preferences MobileContainerManager\nTrollStorePersistenceHelper_CFLAGS = -fobjc-arc -I../Shared -I$(shell brew --prefix)/opt/libarchive/include\n\nifeq ($(LEGACY_CT_BUG),1)\nTrollStorePersistenceHelper_CODESIGN_FLAGS = -Sentitlements.plist -K../legacy.p12\nTrollStorePersistenceHelper_CFLAGS += -DLEGACY_CT_BUG=1\nelse\nTrollStorePersistenceHelper_CODESIGN_FLAGS = --entitlements entitlements.plist\nendif\n\nifeq ($(EMBEDDED_ROOT_HELPER),1)\nTrollStorePersistenceHelper_CFLAGS += -DEMBEDDED_ROOT_HELPER=1\nTrollStorePersistenceHelper_FILES += $(wildcard ../RootHelper/*.m)\nTrollStorePersistenceHelper_LIBRARIES += archive\nTrollStorePersistenceHelper_PRIVATE_FRAMEWORKS += SpringBoardServices BackBoardServices FrontBoardServices\nendif\n\ninclude $(THEOS_MAKE_PATH)/application.mk"
  },
  {
    "path": "TrollHelper/Resources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleExecutable</key>\n\t<string>TrollStorePersistenceHelper</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>TrollHelper</string>\n\t<key>CFBundleIcons</key>\n\t<dict>\n\t\t<key>CFBundlePrimaryIcon</key>\n\t\t<dict>\n\t\t\t<key>CFBundleIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>AppIcon29x29</string>\n\t\t\t\t<string>AppIcon40x40</string>\n\t\t\t\t<string>AppIcon57x57</string>\n\t\t\t\t<string>AppIcon60x60</string>\n\t\t\t</array>\n\t\t\t<key>UIPrerenderedIcon</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</dict>\n\t<key>CFBundleIcons~ipad</key>\n\t<dict>\n\t\t<key>CFBundlePrimaryIcon</key>\n\t\t<dict>\n\t\t\t<key>CFBundleIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>AppIcon29x29</string>\n\t\t\t\t<string>AppIcon40x40</string>\n\t\t\t\t<string>AppIcon57x57</string>\n\t\t\t\t<string>AppIcon60x60</string>\n\t\t\t\t<string>AppIcon50x50</string>\n\t\t\t\t<string>AppIcon72x72</string>\n\t\t\t\t<string>AppIcon76x76</string>\n\t\t\t</array>\n\t\t\t<key>UIPrerenderedIcon</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</dict>\n\t<key>CFBundleIdentifier</key>\n\t<string>com.opa334.trollstorepersistencehelper</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleSupportedPlatforms</key>\n\t<array>\n\t\t<string>iPhoneOS</string>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>2.1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIDeviceFamily</key>\n\t<array>\n\t\t<integer>1</integer>\n\t\t<integer>2</integer>\n\t</array>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>arm64</string>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "TrollHelper/TSHAppDelegateNoScene.h",
    "content": "#import <UIKit/UIKit.h>\n\n@interface TSHAppDelegateNoScene : UIResponder <UIApplicationDelegate>\n@property (nonatomic, strong) UIWindow *window;\n@property (nonatomic, strong) UINavigationController *rootViewController;\n@end"
  },
  {
    "path": "TrollHelper/TSHAppDelegateNoScene.m",
    "content": "#import \"TSHAppDelegateNoScene.h\"\n#import \"TSHRootViewController.h\"\n\n@implementation TSHAppDelegateNoScene\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n\t_window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];\n\t_rootViewController = [[UINavigationController alloc] initWithRootViewController:[[TSHRootViewController alloc] init]];\n\t_window.rootViewController = _rootViewController;\n\t[_window makeKeyAndVisible];\n\treturn YES;\n}\n\n@end\n"
  },
  {
    "path": "TrollHelper/TSHAppDelegateWithScene.h",
    "content": "\n#import <UIKit/UIKit.h>\n\n@interface TSHAppDelegateWithScene : UIResponder <UIApplicationDelegate>\n\n@end"
  },
  {
    "path": "TrollHelper/TSHAppDelegateWithScene.m",
    "content": "#import \"TSHAppDelegateWithScene.h\"\n\n@implementation TSHAppDelegateWithScene\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n\treturn YES;\n}\n\n- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {\n    // Called when a new scene session is being created.\n    // Use this method to select a configuration to create the new scene with.\n    return [[UISceneConfiguration alloc] initWithName:@\"Default Configuration\" sessionRole:connectingSceneSession.role];\n}\n\n\n- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {\n    // Called when the user discards a scene session.\n    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.\n    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.\n}\n\n@end"
  },
  {
    "path": "TrollHelper/TSHRootViewController.h",
    "content": "#import <TSListControllerShared.h>\n\n@interface TSHRootViewController : TSListControllerShared\n{\n    NSString* _newerVersion;\n}\n@end\n"
  },
  {
    "path": "TrollHelper/TSHRootViewController.m",
    "content": "#import \"TSHRootViewController.h\"\n#import <TSUtil.h>\n#import <TSPresentationDelegate.h>\n\n@implementation TSHRootViewController\n\n- (BOOL)isTrollStore\n{\n\treturn NO;\n}\n\n- (void)viewDidLoad\n{\n\t[super viewDidLoad];\n\tTSPresentationDelegate.presentationViewController = self;\n\n\t[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:UIApplicationWillEnterForegroundNotification object:nil];\n\n\tfetchLatestTrollStoreVersion(^(NSString* latestVersion)\n\t{\n\t\tNSString* currentVersion = [self getTrollStoreVersion];\n\t\tNSComparisonResult result = [currentVersion compare:latestVersion options:NSNumericSearch];\n\t\tif(result == NSOrderedAscending)\n\t\t{\n\t\t\t_newerVersion = latestVersion;\n\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t{\n\t\t\t\t[self reloadSpecifiers];\n\t\t\t});\n\t\t}\n\t});\n}\n\n- (NSMutableArray*)specifiers\n{\n\tif(!_specifiers)\n\t{\n\t\t_specifiers = [NSMutableArray new];\n\n\t\t#ifdef LEGACY_CT_BUG\n\t\tNSString* credits = @\"Powered by Fugu15 CoreTrust & installd bugs, thanks to @LinusHenze\\n\\n© 2022-2024 Lars Fröder (opa334)\";\n\t\t#else\n\t\tNSString* credits = @\"Powered by CVE-2023-41991, originally discovered by Google TAG, rediscovered via patchdiffing by @alfiecg_dev\\n\\n© 2022-2024 Lars Fröder (opa334)\";\n\t\t#endif\n\n\t\tPSSpecifier* infoGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\tinfoGroupSpecifier.name = @\"Info\";\n\t\t[_specifiers addObject:infoGroupSpecifier];\n\n\t\tPSSpecifier* infoSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"TrollStore\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:@selector(getTrollStoreInfoString)\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSTitleValueCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\tinfoSpecifier.identifier = @\"info\";\n\t\t[infoSpecifier setProperty:@YES forKey:@\"enabled\"];\n\n\t\t[_specifiers addObject:infoSpecifier];\n\n\t\tBOOL isInstalled = trollStoreAppPath();\n\n\t\tif(_newerVersion && isInstalled)\n\t\t{\n\t\t\t// Update TrollStore\n\t\t\tPSSpecifier* updateTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:[NSString stringWithFormat:@\"Update TrollStore to %@\", _newerVersion]\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\tupdateTrollStoreSpecifier.identifier = @\"updateTrollStore\";\n\t\t\t[updateTrollStoreSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\tupdateTrollStoreSpecifier.buttonAction = @selector(updateTrollStorePressed);\n\t\t\t[_specifiers addObject:updateTrollStoreSpecifier];\n\t\t}\n\n\t\tPSSpecifier* lastGroupSpecifier;\n\n\t\tPSSpecifier* utilitiesGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t[_specifiers addObject:utilitiesGroupSpecifier];\n\n\t\tlastGroupSpecifier = utilitiesGroupSpecifier;\n\n\t\tif(isInstalled || trollStoreInstalledAppContainerPaths().count)\n\t\t{\n\t\t\tPSSpecifier* refreshAppRegistrationsSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Refresh App Registrations\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\trefreshAppRegistrationsSpecifier.identifier = @\"refreshAppRegistrations\";\n\t\t\t[refreshAppRegistrationsSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\trefreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrationsPressed);\n\t\t\t[_specifiers addObject:refreshAppRegistrationsSpecifier];\n\t\t}\n\t\tif(isInstalled)\n\t\t{\n\t\t\tPSSpecifier* uninstallTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Uninstall TrollStore\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\tuninstallTrollStoreSpecifier.identifier = @\"uninstallTrollStore\";\n\t\t\t[uninstallTrollStoreSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t[uninstallTrollStoreSpecifier setProperty:NSClassFromString(@\"PSDeleteButtonCell\") forKey:@\"cellClass\"];\n\t\t\tuninstallTrollStoreSpecifier.buttonAction = @selector(uninstallTrollStorePressed);\n\t\t\t[_specifiers addObject:uninstallTrollStoreSpecifier];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPSSpecifier* installTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Install TrollStore\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\tinstallTrollStoreSpecifier.identifier = @\"installTrollStore\";\n\t\t\t[installTrollStoreSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\tinstallTrollStoreSpecifier.buttonAction = @selector(installTrollStorePressed);\n\t\t\t[_specifiers addObject:installTrollStoreSpecifier];\n\t\t}\n\n\t\tNSString* backupPath = [getExecutablePath() stringByAppendingString:@\"_TROLLSTORE_BACKUP\"];\n\t\tif([[NSFileManager defaultManager] fileExistsAtPath:backupPath])\n\t\t{\n\t\t\tPSSpecifier* uninstallHelperGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t\t[_specifiers addObject:uninstallHelperGroupSpecifier];\n\t\t\tlastGroupSpecifier = uninstallHelperGroupSpecifier;\n\n\t\t\tPSSpecifier* uninstallPersistenceHelperSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Uninstall Persistence Helper\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\tuninstallPersistenceHelperSpecifier.identifier = @\"uninstallPersistenceHelper\";\n\t\t\t[uninstallPersistenceHelperSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t[uninstallPersistenceHelperSpecifier setProperty:NSClassFromString(@\"PSDeleteButtonCell\") forKey:@\"cellClass\"];\n\t\t\tuninstallPersistenceHelperSpecifier.buttonAction = @selector(uninstallPersistenceHelperPressed);\n\t\t\t[_specifiers addObject:uninstallPersistenceHelperSpecifier];\n\t\t}\n\n\t\t#ifdef EMBEDDED_ROOT_HELPER\n\t\tLSApplicationProxy* persistenceHelperProxy = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_ALL);\n\t\tBOOL isRegistered = [persistenceHelperProxy.bundleIdentifier isEqualToString:NSBundle.mainBundle.bundleIdentifier];\n\n\t\tif((isRegistered || !persistenceHelperProxy) && ![[NSFileManager defaultManager] fileExistsAtPath:@\"/Applications/TrollStorePersistenceHelper.app\"])\n\t\t{\n\t\t\tPSSpecifier* registerUnregisterGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t\tlastGroupSpecifier = nil;\n\n\t\t\tNSString* bottomText;\n\t\t\tPSSpecifier* registerUnregisterSpecifier;\n\n\t\t\tif(isRegistered)\n\t\t\t{\n\t\t\t\tbottomText = @\"This app is registered as the TrollStore persistence helper and can be used to fix TrollStore app registrations in case they revert back to \\\"User\\\" state and the apps say they're unavailable.\";\n\t\t\t\tregisterUnregisterSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Unregister Persistence Helper\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t\tregisterUnregisterSpecifier.identifier = @\"registerUnregisterSpecifier\";\n\t\t\t\t[registerUnregisterSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t\t[registerUnregisterSpecifier setProperty:NSClassFromString(@\"PSDeleteButtonCell\") forKey:@\"cellClass\"];\n\t\t\t\tregisterUnregisterSpecifier.buttonAction = @selector(unregisterPersistenceHelperPressed);\n\t\t\t}\n\t\t\telse if(!persistenceHelperProxy)\n\t\t\t{\n\t\t\t\tbottomText = @\"If you want to use this app as the TrollStore persistence helper, you can register it here.\";\n\t\t\t\tregisterUnregisterSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Register Persistence Helper\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t\tregisterUnregisterSpecifier.identifier = @\"registerUnregisterSpecifier\";\n\t\t\t\t[registerUnregisterSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t\tregisterUnregisterSpecifier.buttonAction = @selector(registerPersistenceHelperPressed);\n\t\t\t}\n\n\t\t\t[registerUnregisterGroupSpecifier setProperty:[NSString stringWithFormat:@\"%@\\n\\n%@\", bottomText, credits] forKey:@\"footerText\"];\n\t\t\tlastGroupSpecifier = nil;\n\t\t\t\n\t\t\t[_specifiers addObject:registerUnregisterGroupSpecifier];\n\t\t\t[_specifiers addObject:registerUnregisterSpecifier];\n\t\t}\n\t\t#endif\n\n\t\tif(lastGroupSpecifier)\n\t\t{\n\t\t\t[lastGroupSpecifier setProperty:credits forKey:@\"footerText\"];\n\t\t}\n\t}\n\t\n\t[(UINavigationItem *)self.navigationItem setTitle:@\"TrollStore Helper\"];\n\treturn _specifiers;\n}\n\n- (NSString*)getTrollStoreInfoString\n{\n\tNSString* version = [self getTrollStoreVersion];\n\tif(!version)\n\t{\n\t\treturn @\"Not Installed\";\n\t}\n\telse\n\t{\n\t\treturn [NSString stringWithFormat:@\"Installed, %@\", version];\n\t}\n}\n\n- (void)handleUninstallation\n{\n\t_newerVersion = nil;\n\t[super handleUninstallation];\n}\n\n- (void)registerPersistenceHelperPressed\n{\n\tint ret = spawnRoot(rootHelperPath(), @[@\"register-user-persistence-helper\", NSBundle.mainBundle.bundleIdentifier], nil, nil);\n\tNSLog(@\"registerPersistenceHelperPressed -> %d\", ret);\n\tif(ret == 0)\n\t{\n\t\t[self reloadSpecifiers];\n\t}\n}\n\n- (void)unregisterPersistenceHelperPressed\n{\n\tint ret = spawnRoot(rootHelperPath(), @[@\"uninstall-persistence-helper\"], nil, nil);\n\tif(ret == 0)\n\t{\n\t\t[self reloadSpecifiers];\n\t}\n}\n\n@end\n"
  },
  {
    "path": "TrollHelper/TSHSceneDelegate.h",
    "content": "#import <UIKit/UIKit.h>\n\n@interface TSHSceneDelegate : UIResponder <UIWindowSceneDelegate>\n@property (strong, nonatomic) UIWindow * window;\n@property (nonatomic, strong) UINavigationController *rootViewController;\n@end"
  },
  {
    "path": "TrollHelper/TSHSceneDelegate.m",
    "content": "#import \"TSHSceneDelegate.h\"\n#import \"TSHRootViewController.h\"\n\n@implementation TSHSceneDelegate\n\n- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {\n\t// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.\n\t// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.\n\t// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).\n\n\tUIWindowScene* windowScene = (UIWindowScene*)scene;\n\t_window = [[UIWindow alloc] initWithWindowScene:windowScene];\n\t_rootViewController = [[UINavigationController alloc] initWithRootViewController:[[TSHRootViewController alloc] init]];\n\t_window.rootViewController = _rootViewController;\n\t[_window makeKeyAndVisible];\n}\n\n- (void)sceneDidDisconnect:(UIScene *)scene {\n\t// Called as the scene is being released by the system.\n\t// This occurs shortly after the scene enters the background, or when its session is discarded.\n\t// Release any resources associated with this scene that can be re-created the next time the scene connects.\n\t// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).\n}\n\n\n- (void)sceneDidBecomeActive:(UIScene *)scene {\n\t// Called when the scene has moved from an inactive state to an active state.\n\t// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.\n}\n\n\n- (void)sceneWillResignActive:(UIScene *)scene {\n\t// Called when the scene will move from an active state to an inactive state.\n\t// This may occur due to temporary interruptions (ex. an incoming phone call).\n}\n\n\n- (void)sceneWillEnterForeground:(UIScene *)scene {\n\t// Called as the scene transitions from the background to the foreground.\n\t// Use this method to undo the changes made on entering the background.\n}\n\n\n- (void)sceneDidEnterBackground:(UIScene *)scene {\n\t// Called as the scene transitions from the foreground to the background.\n\t// Use this method to save data, release shared resources, and store enough scene-specific state information\n\t// to restore the scene back to its current state.\n}\n\n- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts\n{\n}\n\n@end\n"
  },
  {
    "path": "TrollHelper/control",
    "content": "Package: com.opa334.trollstorehelper\nName: TrollStore Helper\nVersion: 2.1\nArchitecture: iphoneos-arm\nDescription: Helper utility to install and manage TrollStore!\nMaintainer: opa334\nAuthor: opa334\nSection: Applications\n"
  },
  {
    "path": "TrollHelper/entitlements.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>application-identifier</key>\n\t<string>com.opa334.trollstorepersistencehelper</string>\n\t<key>com.apple.CommCenter.fine-grained</key>\n\t<array>\n\t\t<string>data-allowed-write</string>\n\t</array>\n\t<key>com.apple.private.persona-mgmt</key>\n\t<true/>\n\n\t<!-- All entitlements from RootHelper except for com.apple.private.security.container-required=false -->\n\t<key>platform-application</key>\n\t<true/>\n\t<key>com.apple.private.security.no-sandbox</key>\n\t<true/>\n\t<key>com.apple.private.security.container-manager</key>\n\t<true/>\n\t<key>com.apple.private.MobileContainerManager.allowed</key>\n\t<true/>\n\t<key>com.apple.private.coreservices.canmaplsdatabase</key>\n\t<true/>\n\t<key>com.apple.lsapplicationworkspace.rebuildappdatabases</key>\n\t<true/>\n\t<key>com.apple.private.security.storage.AppBundles</key>\n\t<true/>\n\t<key>com.apple.private.security.storage.MobileDocuments</key>\n\t<true/>\n\t<key>com.apple.private.security.storage-exempt.heritable</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.allowed</key>\n\t<true/>\n\t<key>com.apple.private.uninstall.deletion</key>\n\t<true/>\n\t<key>com.apple.springboard.launchapplications</key>\n\t<true/>\n\t<key>com.apple.backboardd.launchapplications</key>\n\t<true/>\n\t<key>com.apple.frontboard.launchapplications</key>\n\t<true/>\n\t<key>com.apple.multitasking.termination</key>\n\t<true/>\n\t<key>com.apple.private.mobileinstall.allowedSPI</key>\n\t<array>\n\t\t<string>InstallForLaunchServices</string>\n\t\t<string>Install</string>\n\t\t<string>UninstallForLaunchServices</string>\n\t\t<string>Uninstall</string>\n\t\t<string>UpdatePlaceholderMetadata</string>\n\t</array>\n</dict>\n</plist>"
  },
  {
    "path": "TrollHelper/main.m",
    "content": "#import <Foundation/Foundation.h>\n#import \"TSHAppDelegateNoScene.h\"\n#import \"TSHAppDelegateWithScene.h\"\n#import \"TSHSceneDelegate.h\"\n#import <TSUtil.h>\n#import <objc/runtime.h>\n\nBOOL sceneDelegateFix(void)\n{\n\tNSString* sceneDelegateClassName = nil;\n\t\n\tNSDictionary* UIApplicationSceneManifest = [NSBundle.mainBundle objectForInfoDictionaryKey:@\"UIApplicationSceneManifest\"];\n\tif(UIApplicationSceneManifest && [UIApplicationSceneManifest isKindOfClass:NSDictionary.class])\n\t{\n\t\tNSDictionary* UISceneConfiguration = UIApplicationSceneManifest[@\"UISceneConfigurations\"];\n\t\tif(UISceneConfiguration && [UISceneConfiguration isKindOfClass:NSDictionary.class])\n\t\t{\n\t\t\tNSArray* UIWindowSceneSessionRoleApplication = UISceneConfiguration[@\"UIWindowSceneSessionRoleApplication\"];\n\t\t\tif(UIWindowSceneSessionRoleApplication && [UIWindowSceneSessionRoleApplication isKindOfClass:NSArray.class])\n\t\t\t{\n\t\t\t\tNSDictionary* sceneToUse = nil;\n\t\t\t\tif(UIWindowSceneSessionRoleApplication.count > 1)\n\t\t\t\t{\n\t\t\t\t\tfor(NSDictionary* scene in UIWindowSceneSessionRoleApplication)\n\t\t\t\t\t{\n\t\t\t\t\t\tif([scene isKindOfClass:NSDictionary.class])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tNSString* UISceneConfigurationName = scene[@\"UISceneConfigurationName\"];\n\t\t\t\t\t\t\tif([UISceneConfigurationName isKindOfClass:NSString.class])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif([UISceneConfigurationName isEqualToString:@\"Default Configuration\"])\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsceneToUse = scene;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif(!sceneToUse)\n\t\t\t\t\t{\n\t\t\t\t\t\tsceneToUse = UIWindowSceneSessionRoleApplication.firstObject;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsceneToUse = UIWindowSceneSessionRoleApplication.firstObject;\n\t\t\t\t}\n\n\t\t\t\tif(sceneToUse && [sceneToUse isKindOfClass:NSDictionary.class])\n\t\t\t\t{\n\t\t\t\t\tsceneDelegateClassName = sceneToUse[@\"UISceneDelegateClassName\"];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif(sceneDelegateClassName && [sceneDelegateClassName isKindOfClass:NSString.class])\n\t{\n\t\tClass newClass = objc_allocateClassPair([TSHSceneDelegate class], sceneDelegateClassName.UTF8String, 0);\n\t\tobjc_registerClassPair(newClass);\n\t\treturn YES;\n\t}\n\n\treturn NO;\n}\n\nint main(int argc, char *argv[], char *envp[]) {\n\t@autoreleasepool {\n\t\t#ifdef EMBEDDED_ROOT_HELPER\n\t\textern int rootHelperMain(int argc, char *argv[], char *envp[]);\n\t\tif(getuid() == 0)\n\t\t{\n\t\t\t// I got this idea while taking a dump\n\t\t\t// Don't judge\n\t\t\treturn rootHelperMain(argc, argv, envp);\n\t\t}\n\t\t#endif\n\n\t\tchineseWifiFixup();\n\t\tif(sceneDelegateFix())\n\t\t{\n\t\t\treturn UIApplicationMain(argc, argv, nil, NSStringFromClass(TSHAppDelegateWithScene.class));\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn UIApplicationMain(argc, argv, nil, NSStringFromClass(TSHAppDelegateNoScene.class));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TrollStore/Makefile",
    "content": "TARGET := iphone:clang:16.5:14.0\nINSTALL_TARGET_PROCESSES = TrollStore\nARCHS = arm64\n\nTARGET_CODESIGN = ../Exploits/fastPathSign/fastPathSign\n\ninclude $(THEOS)/makefiles/common.mk\n\nAPPLICATION_NAME = TrollStore\n\nTrollStore_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m)\nTrollStore_FRAMEWORKS = UIKit CoreGraphics CoreServices CoreTelephony\nTrollStore_PRIVATE_FRAMEWORKS = Preferences MobileIcons MobileContainerManager\nTrollStore_LIBRARIES = archive\nTrollStore_CFLAGS = -fobjc-arc -I../Shared -I$(shell brew --prefix)/opt/libarchive/include\nTrollStore_CODESIGN_FLAGS = --entitlements entitlements.plist\n\ninclude $(THEOS_MAKE_PATH)/application.mk\n"
  },
  {
    "path": "TrollStore/Resources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleExecutable</key>\n\t<string>TrollStore</string>\n\t<key>CFBundleIcons</key>\n\t<dict>\n\t\t<key>CFBundlePrimaryIcon</key>\n\t\t<dict>\n\t\t\t<key>CFBundleIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>AppIcon29x29</string>\n\t\t\t\t<string>AppIcon40x40</string>\n\t\t\t\t<string>AppIcon57x57</string>\n\t\t\t\t<string>AppIcon60x60</string>\n\t\t\t</array>\n\t\t\t<key>UIPrerenderedIcon</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</dict>\n\t<key>CFBundleIcons~ipad</key>\n\t<dict>\n\t\t<key>CFBundlePrimaryIcon</key>\n\t\t<dict>\n\t\t\t<key>CFBundleIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>AppIcon29x29</string>\n\t\t\t\t<string>AppIcon40x40</string>\n\t\t\t\t<string>AppIcon57x57</string>\n\t\t\t\t<string>AppIcon60x60</string>\n\t\t\t\t<string>AppIcon50x50</string>\n\t\t\t\t<string>AppIcon72x72</string>\n\t\t\t\t<string>AppIcon76x76</string>\n\t\t\t</array>\n\t\t\t<key>UIPrerenderedIcon</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</dict>\n\t<key>CFBundleIdentifier</key>\n\t<string>com.opa334.TrollStore</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleSupportedPlatforms</key>\n\t<array>\n\t\t<string>iPhoneOS</string>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>2.1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIDeviceFamily</key>\n\t<array>\n\t\t<integer>1</integer>\n\t\t<integer>2</integer>\n\t</array>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<false/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Default Configuration</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>TSSceneDelegate</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n\t<key>UTImportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>iOS App</string>\n\t\t\t<key>UTTypeIconFiles</key>\n\t\t\t<array/>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.apple.itunes.ipa</string>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>ipa</string>\n\t\t\t\t</array>\n\t\t\t\t<key>public.mime-type</key>\n\t\t\t\t<array/>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleDocumentTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>iOS App</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Default</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>com.apple.itunes.ipa</string>\n\t\t\t</array>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>TrollStore Update</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Default</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>public.tar-archive</string>\n\t\t\t</array>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>AirDrop friendly iOS app</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Viewer</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Owner</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>com.opa334.trollstore.tipa</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>UTExportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.opa334.trollstore.tipa</string>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>AirDrop friendly iOS app</string>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>tipa</string>\n\t\t\t\t</array>\n\t\t\t\t<key>public.mime-type</key>\n\t\t\t\t<string>application/trollstore-ipa</string>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>com.apple.Magnifier</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>apple-magnifier</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>LSSupportsOpeningDocumentsInPlace</key>\n\t<false/>\n\t<key>TSRootBinaries</key>\n\t<array>\n\t\t<string>trollstorehelper</string>\n\t\t<string>ldid</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "TrollStore/TSAppDelegate.h",
    "content": "#import <UIKit/UIKit.h>\n\n@interface TSAppDelegate : UIResponder <UIApplicationDelegate>\n\n@end\n"
  },
  {
    "path": "TrollStore/TSAppDelegate.m",
    "content": "#import \"TSAppDelegate.h\"\n#import \"TSRootViewController.h\"\n\n@implementation TSAppDelegate\n\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n\treturn YES;\n}\n\n- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {\n    // Called when a new scene session is being created.\n    // Use this method to select a configuration to create the new scene with.\n    return [[UISceneConfiguration alloc] initWithName:@\"Default Configuration\" sessionRole:connectingSceneSession.role];\n}\n\n\n- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {\n    // Called when the user discards a scene session.\n    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.\n    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.\n}\n\n@end\n"
  },
  {
    "path": "TrollStore/TSAppInfo.h",
    "content": "//\n//  TSIPAInfo.h\n//  IPAInfo\n//\n//  Created by Lars Fröder on 22.10.22.\n//\n\n#import <Foundation/Foundation.h>\n#import <archive.h>\n#import <archive_entry.h>\n@import UIKit;\n\n@interface TSAppInfo : NSObject\n{\n\tNSString* _path;\n\tBOOL _isArchive;\n\tstruct archive* _archive;\n\t\n\tNSString* _cachedAppBundleName;\n\tNSString* _cachedRegistrationState;\n\tNSDictionary* _cachedInfoDictionary;\n\tNSDictionary* _cachedInfoDictionariesByPluginSubpaths;\n\tNSDictionary* _cachedEntitlementsByBinarySubpaths;\n\tUIImage* _cachedPreviewIcon;\n\tint64_t _cachedSize;\n}\n\n- (instancetype)initWithIPAPath:(NSString*)ipaPath;\n- (instancetype)initWithAppBundlePath:(NSString*)bundlePath;\n- (NSError*)determineAppBundleName;\n- (NSError*)loadInfoDictionary;\n- (NSError*)loadEntitlements;\n- (NSError*)loadPreviewIcon;\n\n- (NSError*)sync_loadBasicInfo;\n- (NSError*)sync_loadInfo;\n\n- (void)loadBasicInfoWithCompletion:(void (^)(NSError*))completionHandler;\n- (void)loadInfoWithCompletion:(void (^)(NSError*))completionHandler;\n\n- (NSString*)displayName;\n- (NSString*)bundleIdentifier;\n- (NSString*)versionString;\n- (NSString*)sizeString;\n- (NSString*)bundlePath;\n- (NSString*)registrationState;\n\n- (UIImage*)iconForSize:(CGSize)size;\n\n- (NSAttributedString*)detailedInfoTitle;\n- (NSAttributedString*)detailedInfoDescription;\n//- (UIImage*)image;\n- (BOOL)isDebuggable;\n- (void)log;\n\n@end\n"
  },
  {
    "path": "TrollStore/TSAppInfo.m",
    "content": "#import \"TSAppInfo.h\"\n#import \"TSCommonTCCServiceNames.h\"\n#import <TSUtil.h>\n\nextern CGImageRef LICreateIconForImage(CGImageRef image, int variant, int precomposed);\nextern UIImage* imageWithSize(UIImage* image, CGSize size);\n\n@implementation TSAppInfo\n\n- (instancetype)initWithIPAPath:(NSString*)ipaPath\n{\n\tself = [super init];\n\t\n\tif(self)\n\t{\n\t\t_path = ipaPath;\n\t\t_isArchive = YES;\n\t\t_archive = nil;\n\t}\n\t\n\treturn self;\n}\n\n- (instancetype)initWithAppBundlePath:(NSString*)bundlePath\n{\n\tself = [super init];\n\t\n\tif(self)\n\t{\n\t\t_path = bundlePath;\n\t\t_isArchive = NO;\n\t\t_archive = nil;\n\t}\n\t\n\treturn self;\n}\n\n- (void)dealloc\n{\n\t[self closeArchive];\n}\n\n- (void)enumerateArchive:(void (^)(struct archive_entry* entry, BOOL* stop))enumerateBlock\n{\n\t[self openArchive];\n\n\tstruct archive_entry *entry;\n\tint r;\n\tfor (;;)\n\t{\n\t\tr = archive_read_next_header(_archive, &entry);\n\t\tif (r == ARCHIVE_EOF)\n\t\t\tbreak;\n\t\tif (r < ARCHIVE_OK)\n\t\t\tfprintf(stderr, \"%s\\n\", archive_error_string(_archive));\n\t\tif (r < ARCHIVE_WARN)\n\t\t\treturn;\n\t\t\n\t\tBOOL stop = NO;\n\t\tenumerateBlock(entry, &stop);\n\t\tif(stop) break;\n\t}\n}\n\n- (struct archive_entry*)archiveEntryForSubpath:(NSString*)subpath\n{\n\t__block struct archive_entry* outEntry = nil;\n\t[self enumerateArchive:^(struct archive_entry *entry, BOOL *stop) {\n\t\tNSString* currentSubpath = [NSString stringWithUTF8String:archive_entry_pathname(entry)];\n\t\tif([currentSubpath isEqualToString:subpath])\n\t\t{\n\t\t\toutEntry = entry;\n\t\t\t*stop = YES;\n\t\t}\n\t}];\n\treturn outEntry;\n}\n\n- (NSError*)determineAppBundleName\n{\n\tNSError* outError;\n\n\tif(!_cachedAppBundleName)\n\t{\n\t\tif(_isArchive)\n\t\t{\n\t\t\t[self enumerateArchive:^(struct archive_entry *entry, BOOL *stop)\n\t\t\t{\n\t\t\t\tNSString* currentSubpath = [NSString stringWithUTF8String:archive_entry_pathname(entry)];\n\t\t\t\tif(currentSubpath.pathComponents.count == 3)\n\t\t\t\t{\n\t\t\t\t\tif([currentSubpath.pathComponents[0] isEqualToString:@\"Payload\"] && [currentSubpath.pathComponents[1].pathExtension isEqualToString:@\"app\"])\n\t\t\t\t\t{\n\t\t\t\t\t\tself->_cachedAppBundleName = currentSubpath.pathComponents[1];\n\t\t\t\t\t\t*stop = YES;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}];\n\n\t\t\tif(!_cachedAppBundleName)\n\t\t\t{\n\t\t\t\tNSString* errorDescription = @\"Unable to locate app bundle inside the .IPA archive.\";\n\t\t\t\toutError = [NSError errorWithDomain:TrollStoreErrorDomain code:301 userInfo:@{NSLocalizedDescriptionKey : errorDescription}];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn outError;\n}\n\n- (NSError*)loadInfoDictionary\n{\n\tif(_isArchive && _cachedAppBundleName)\n\t{\n\t\tNSString* mainInfoPlistPath = [NSString stringWithFormat:@\"Payload/%@/Info.plist\", _cachedAppBundleName];\n\t\tstruct archive_entry* infoDictEntry = [self archiveEntryForSubpath:mainInfoPlistPath];\n\t\tif(infoDictEntry)\n\t\t{\n\t\t\tsize_t size = archive_entry_size(infoDictEntry);\n\t\t\tvoid* buf = malloc(size);\n\t\t\tsize_t read = archive_read_data(_archive, buf, size);\n\t\t\t\n\t\t\tif(read == size)\n\t\t\t{\n\t\t\t\tNSData* infoPlistData = [NSData dataWithBytes:buf length:size];\n\t\t\t\t_cachedInfoDictionary = [NSPropertyListSerialization propertyListWithData:infoPlistData options:NSPropertyListImmutable format:nil error:nil];\n\t\t\t}\n\t\t\tfree(buf);\n\t\t}\n\t\t\n\t\t__block NSMutableDictionary* pluginInfoDictionaries = [NSMutableDictionary new];\n\t\t\n\t\t[self enumerateArchive:^(struct archive_entry *entry, BOOL *stop) {\n\t\t\tNSString* currentSubpath = [NSString stringWithUTF8String:archive_entry_pathname(entry)];\n\t\t\tif([currentSubpath isEqualToString:mainInfoPlistPath]) return;\n\t\t\t\n\t\t\tif([currentSubpath.lastPathComponent isEqualToString:@\"Info.plist\"] && currentSubpath.pathComponents.count == 5)\n\t\t\t{\n\t\t\t\tif([currentSubpath.pathComponents[2] isEqualToString:@\"PlugIns\"])\n\t\t\t\t{\n\t\t\t\t\tsize_t size = archive_entry_size(entry);\n\t\t\t\t\tvoid* buf = malloc(size);\n\t\t\t\t\tsize_t read = archive_read_data(self->_archive, buf, size);\n\t\t\t\t\t\n\t\t\t\t\tif(read == size)\n\t\t\t\t\t{\n\t\t\t\t\t\tNSData* infoPlistData = [NSData dataWithBytes:buf length:size];\n\t\t\t\t\t\tNSDictionary* pluginPlist = [NSPropertyListSerialization propertyListWithData:infoPlistData options:NSPropertyListImmutable format:nil error:nil];\n\t\t\t\t\t\tpluginInfoDictionaries[currentSubpath.stringByDeletingLastPathComponent] = pluginPlist;\n\t\t\t\t\t}\n\t\t\t\t\tfree(buf);\n\t\t\t\t}\n\t\t\t}\n\t\t}];\n\t\t\n\t\t_cachedInfoDictionariesByPluginSubpaths = pluginInfoDictionaries.copy;\n\t}\n\telse\n\t{\n\t\tNSString* mainInfoPlistPath = [_path stringByAppendingPathComponent:@\"Info.plist\"];\n\t\tif([[NSFileManager defaultManager] fileExistsAtPath:mainInfoPlistPath])\n\t\t{\n\t\t\t_cachedInfoDictionary = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:mainInfoPlistPath] error:nil];\n\t\t}\n\n\t\t__block NSMutableDictionary* pluginInfoDictionaries = [NSMutableDictionary new];\n\t\tNSArray* plugIns = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[_path stringByAppendingPathComponent:@\"PlugIns\"] error:nil];\n\t\tfor(NSString* plugIn in plugIns)\n\t\t{\n\t\t\tNSString* pluginSubpath = [NSString stringWithFormat:@\"PlugIns/%@\", plugIn];\n\t\t\tNSString* pluginInfoDictionaryPath = [[_path stringByAppendingPathComponent:pluginSubpath] stringByAppendingPathComponent:@\"Info.plist\"];\n\t\t\tNSDictionary* pluginInfoDictionary = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:pluginInfoDictionaryPath] error:nil];\n\t\t\tif(pluginInfoDictionary)\n\t\t\t{\n\t\t\t\tpluginInfoDictionaries[pluginSubpath] = pluginInfoDictionary;\n\t\t\t}\n\t\t}\n\n\t\t_cachedInfoDictionariesByPluginSubpaths = pluginInfoDictionaries.copy;\n\t}\n\n\tif(!_cachedInfoDictionary)\n\t{\n\t\tNSString* errorDescription = @\"Unable to locate Info.plist inside app bundle.\";\n\t\treturn [NSError errorWithDomain:TrollStoreErrorDomain code:302 userInfo:@{NSLocalizedDescriptionKey : errorDescription}];\n\t}\n\t\n\treturn nil;\n}\n\n- (NSError*)loadInstalledState\n{\n\tif(!_isArchive)\n\t{\n\t\tNSURL* bundleURL = [NSURL fileURLWithPath:_path];\n\t\tLSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForBundleURL:bundleURL];\n\t\tif(appProxy)\n\t\t{\n\t\t\tif(appProxy && appProxy.isInstalled)\n\t\t\t{\n\t\t\t\t_cachedRegistrationState = appProxy.applicationType;\n\t\t\t}\n\t\t}\n\t}\n\treturn nil;\n}\n\n- (NSError*)loadEntitlements\n{\n\tif(!_cachedEntitlementsByBinarySubpaths)\n\t{\n\t\tNSMutableDictionary* entitlementsByBinarySubpaths = [NSMutableDictionary new];\n\n\t\tif(_isArchive)\n\t\t{\n\t\t\tif(_cachedInfoDictionary)\n\t\t\t{\n\t\t\t\tNSString* bundleExecutable = _cachedInfoDictionary[@\"CFBundleExecutable\"];\n\t\t\t\tNSString* bundleExecutableSubpath = [NSString stringWithFormat:@\"Payload/%@/%@\", _cachedAppBundleName, bundleExecutable];\n\t\t\t\tstruct archive_entry* mainBinaryEntry = [self archiveEntryForSubpath:bundleExecutableSubpath];\n\t\t\t\tif(!mainBinaryEntry)\n\t\t\t\t{\n\t\t\t\t\tNSString* errorDescription = @\"Unable to locate main binary inside app bundle.\";\n\t\t\t\t\treturn [NSError errorWithDomain:TrollStoreErrorDomain code:303 userInfo:@{NSLocalizedDescriptionKey : errorDescription}];\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tsize_t size = archive_entry_size(mainBinaryEntry);\n\t\t\t\tvoid* buf = malloc(size);\n\t\t\t\tsize_t read = archive_read_data(_archive, buf, size);\n\t\t\t\t\n\t\t\t\tif(read == size)\n\t\t\t\t{\n\t\t\t\t\tNSData* binaryData = [NSData dataWithBytes:buf length:size];\n\t\t\t\t\tentitlementsByBinarySubpaths[bundleExecutableSubpath] = dumpEntitlementsFromBinaryData(binaryData);\n\t\t\t\t}\n\t\t\t\tfree(buf);\n\t\t\t}\n\t\t\t\n\t\t\t[_cachedInfoDictionariesByPluginSubpaths enumerateKeysAndObjectsUsingBlock:^(NSString* pluginSubpath, NSDictionary* infoDictionary, BOOL * _Nonnull stop) {\n\t\t\t\tNSString* pluginExecutable = infoDictionary[@\"CFBundleExecutable\"];\n\t\t\t\tNSString* pluginExecutableSubpath = [NSString stringWithFormat:@\"%@/%@\", pluginSubpath, pluginExecutable];\n\t\t\t\tstruct archive_entry* pluginBinaryEntry = [self archiveEntryForSubpath:pluginExecutableSubpath];\n\t\t\t\tif(!pluginBinaryEntry) return;\n\t\t\t\t\n\t\t\t\tsize_t size = archive_entry_size(pluginBinaryEntry);\n\t\t\t\tvoid* buf = malloc(size);\n\t\t\t\tsize_t read = archive_read_data(_archive, buf, size);\n\t\t\t\t\n\t\t\t\tif(read == size)\n\t\t\t\t{\n\t\t\t\t\tNSData* binaryData = [NSData dataWithBytes:buf length:size];\n\t\t\t\t\tentitlementsByBinarySubpaths[pluginExecutableSubpath] = dumpEntitlementsFromBinaryData(binaryData);\n\t\t\t\t}\n\t\t\t\tfree(buf);\n\t\t\t}];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif(_cachedInfoDictionary)\n\t\t\t{\n\t\t\t\tNSString* bundleExecutable = _cachedInfoDictionary[@\"CFBundleExecutable\"];\n\t\t\t\tNSString* bundleExecutablePath = [_path stringByAppendingPathComponent:bundleExecutable];\n\n\t\t\t\tif(![[NSFileManager defaultManager] fileExistsAtPath:bundleExecutablePath])\n\t\t\t\t{\n\t\t\t\t\tNSString* errorDescription = @\"Unable to locate main binary inside app bundle.\";\n\t\t\t\t\treturn [NSError errorWithDomain:TrollStoreErrorDomain code:303 userInfo:@{NSLocalizedDescriptionKey : errorDescription}];\n\t\t\t\t}\n\n\t\t\t\tentitlementsByBinarySubpaths[bundleExecutable] = dumpEntitlementsFromBinaryAtPath(bundleExecutablePath);\n\t\t\t}\n\n\t\t\t[_cachedInfoDictionariesByPluginSubpaths enumerateKeysAndObjectsUsingBlock:^(NSString* pluginSubpath, NSDictionary* infoDictionary, BOOL * _Nonnull stop) {\n\t\t\t\tNSString* pluginExecutable = infoDictionary[@\"CFBundleExecutable\"];\n\t\t\t\tNSString* pluginExecutableSubpath = [NSString stringWithFormat:@\"%@/%@\", pluginSubpath, pluginExecutable];\n\n\t\t\t\tNSString* pluginExecutablePath = [_path stringByAppendingPathComponent:pluginExecutableSubpath];\n\t\t\t\tentitlementsByBinarySubpaths[pluginExecutableSubpath] = dumpEntitlementsFromBinaryAtPath(pluginExecutablePath);\n\t\t\t}];\n\t\t}\n\n\t\t_cachedEntitlementsByBinarySubpaths = entitlementsByBinarySubpaths.copy;\n\t}\n\treturn 0;\n}\n\n- (NSError*)loadSize\n{\n\t_cachedSize = 0;\n\n\tif(_isArchive)\n\t{\n\t\t[self enumerateArchive:^(struct archive_entry* entry, BOOL* stop)\n\t\t{\n\t\t\tint64_t size = archive_entry_size(entry);\n\t\t\t_cachedSize += size;\n\t\t}];\n\t}\n\telse\n\t{\n\t\tNSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:_path]\n\t\t\t\t\t     includingPropertiesForKeys:@[NSURLIsRegularFileKey,NSURLFileAllocatedSizeKey,NSURLTotalFileAllocatedSizeKey]\n\t\t\t\t\t     options:0\n\t\t\t\t\t     errorHandler:nil];\n\n\t\tfor(NSURL* itemURL in enumerator)\n\t\t{\n\t\t\tNSNumber* isRegularFile;\n\t\t\tNSError* error;\n\t\t\t[itemURL getResourceValue:&isRegularFile forKey:NSURLIsRegularFileKey error:&error];\n\n\t\t\tif(isRegularFile.boolValue)\n\t\t\t{\n\t\t\t\tNSNumber* totalFileAllocatedSize;\n\t\t\t\t[itemURL getResourceValue:&totalFileAllocatedSize forKey:NSURLTotalFileAllocatedSizeKey error:nil];\n\t\t\t\tif(totalFileAllocatedSize)\n\t\t\t\t{\n\t\t\t\t\t_cachedSize += totalFileAllocatedSize.integerValue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tNSNumber* fileAllocatedSize;\n\t\t\t\t\t[itemURL getResourceValue:&fileAllocatedSize forKey:NSURLFileAllocatedSizeKey error:nil];\n\t\t\t\t\tif(fileAllocatedSize)\n\t\t\t\t\t{\n\t\t\t\t\t\t_cachedSize += fileAllocatedSize.integerValue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil;\n}\n\n- (NSError*)loadPreviewIcon\n{\n\tint imageVariant;\n\tCGFloat screenScale = UIScreen.mainScreen.scale;\n\n\tif(screenScale >= 3.0)\n\t{\n\t\timageVariant = 34;\n\t}\n\telse if(screenScale >= 2.0)\n\t{\n\t\timageVariant = 17;\n\t}\n\telse\n\t{\n\t\timageVariant = 4;\n\t}\n\n\tCGImageRef liIcon = LICreateIconForImage([[self iconForSize:CGSizeMake(29,29)] CGImage], imageVariant, 0);\n\t_cachedPreviewIcon = [[UIImage alloc] initWithCGImage:liIcon scale:screenScale orientation:0];;\n\treturn nil;\n}\n\n- (int)openArchive\n{\n\tif(_archive)\n\t{\n\t\t[self closeArchive];\n\t}\n\t_archive = archive_read_new();\n\tarchive_read_support_format_all(_archive);\n\tarchive_read_support_filter_all(_archive);\n\tint r = archive_read_open_filename(_archive, _path.fileSystemRepresentation, 10240);\n\treturn r ? r : 0;\n}\n\n- (void)closeArchive\n{\n\tif(_archive)\n\t{\n\t\tarchive_read_close(_archive);\n\t\tarchive_read_free(_archive);\n\t\t_archive = nil;\n\t}\n}\n\n- (NSError*)sync_loadBasicInfo\n{\n\tNSError* e;\n\t\n\te = [self determineAppBundleName];\n\tif(e) return e;\n\t\n\te = [self loadInfoDictionary];\n\tif(e) return e;\n\n\te = [self loadInstalledState];\n\tif(e) return e;\n\n\treturn nil;\n}\n\n- (NSError*)sync_loadInfo\n{\n\tNSError* e;\n\n\te = [self sync_loadBasicInfo];\n\tif(e) return e;\n\t\n\te = [self loadEntitlements];\n\tif(e) return e;\n\n\te = [self loadSize];\n\tif(e) return e;\n\n\te = [self loadPreviewIcon];\n\tif(e) return e;\n\n\treturn nil;\n}\n\n- (void)loadBasicInfoWithCompletion:(void (^)(NSError*))completionBlock\n{\n\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n\t\tif(completionBlock) completionBlock([self sync_loadBasicInfo]);\n\t});\n}\n\n- (void)loadInfoWithCompletion:(void (^)(NSError*))completionBlock\n{\n\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n\t\tif(completionBlock) completionBlock([self sync_loadInfo]);\n\t});\n}\n\n- (void)enumerateAllInfoDictionaries:(void (^)(NSString* key, NSObject* value, BOOL* stop))enumerateBlock\n{\n\tif(!enumerateBlock) return;\n\n\t__block BOOL b_stop = NO;\n\t\n\t[_cachedInfoDictionary enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSObject* value, BOOL* stop) {\n\t\tenumerateBlock(key, value, &b_stop);\n\t\tif(b_stop) *stop = YES;\n\t}];\n\t\n\tif(b_stop) return;\n\t\n\t[_cachedInfoDictionariesByPluginSubpaths enumerateKeysAndObjectsUsingBlock:^(NSString* pluginSubpath, NSDictionary* pluginInfoDictionary, BOOL* stop_1)\n\t{\n\t\tif([pluginInfoDictionary isKindOfClass:NSDictionary.class])\n\t\t{\n\t\t\t[pluginInfoDictionary enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSObject* value, BOOL * _Nonnull stop_2) {\n\t\t\t\tenumerateBlock(key, value, &b_stop);\n\t\t\t\tif(b_stop)\n\t\t\t\t{\n\t\t\t\t\t*stop_1 = YES;\n\t\t\t\t\t*stop_2 = YES;\n\t\t\t\t}\n\t\t\t}];\n\t\t}\n\t}];\n}\n\n- (void)enumerateAllEntitlements:(void (^)(NSString* key, NSObject* value, BOOL* stop))enumerateBlock\n{\n\tif(!enumerateBlock) return;\n\n\t__block BOOL b_stop = NO;\n\t\n\t[_cachedEntitlementsByBinarySubpaths enumerateKeysAndObjectsUsingBlock:^(NSString* binarySubpath, NSDictionary* binaryInfoDictionary, BOOL* stop_1)\n\t{\n\t\tif([binaryInfoDictionary isKindOfClass:NSDictionary.class])\n\t\t{\n\t\t\t[binaryInfoDictionary enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSObject* value, BOOL * _Nonnull stop_2) {\n\t\t\t\tenumerateBlock(key, value, &b_stop);\n\t\t\t\tif(b_stop)\n\t\t\t\t{\n\t\t\t\t\t*stop_1 = YES;\n\t\t\t\t\t*stop_2 = YES;\n\t\t\t\t}\n\t\t\t}];\n\t\t}\n\t}];\n}\n\n- (void)enumerateAvailableIcons:(void (^)(CGSize iconSize, NSUInteger iconScale, NSString* iconPath, BOOL* stop))enumerateBlock\n{\n\tif(!enumerateBlock) return;\n\n\tif(_cachedInfoDictionary)\n\t{\n\t\tNSString* iconName = nil;\n\t\tNSDictionary* cfBundleIcons = _cachedInfoDictionary[@\"CFBundleIcons\"];\n\t\tif(!cfBundleIcons)\n\t\t{\n\t\t\tcfBundleIcons = _cachedInfoDictionary[@\"CFBundleIcons~ipad\"];\n\t\t}\n\t\tif(cfBundleIcons && [cfBundleIcons isKindOfClass:NSDictionary.class])\n\t\t{\n\t\t\tNSDictionary* cfBundlePrimaryIcon = cfBundleIcons[@\"CFBundlePrimaryIcon\"];\n\t\t\t\n\t\t\tif(cfBundlePrimaryIcon && [cfBundlePrimaryIcon isKindOfClass:NSDictionary.class])\n\t\t\t{\n\t\t\t\tNSString* potentialIconName = cfBundlePrimaryIcon[@\"CFBundleIconName\"];\n\t\t\t\tif(potentialIconName && [potentialIconName isKindOfClass:NSString.class])\n\t\t\t\t{\n\t\t\t\t\ticonName = potentialIconName;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tNSArray* cfBundleIconFiles = cfBundlePrimaryIcon[@\"CFBundleIconFiles\"];\n\t\t\t\t\tif(cfBundleIconFiles && [cfBundleIconFiles isKindOfClass:NSArray.class])\n\t\t\t\t\t{\n\t\t\t\t\t\tNSString* oneIconFile = cfBundleIconFiles.firstObject;\n\t\t\t\t\t\tNSString* otherIconFile = cfBundleIconFiles.lastObject;\n\t\t\t\t\t\ticonName = [oneIconFile commonPrefixWithString:otherIconFile options:NSLiteralSearch];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif(!iconName) return;\n\n\t\tvoid (^wrapperBlock)(NSString* iconPath, BOOL* stop) = ^(NSString* iconPath, BOOL* stop)\n\t\t{\n\t\t\tNSString* currentIconName = iconPath.lastPathComponent;\n\t\t\tNSString* iconSuffix = [currentIconName substringFromIndex:[iconName length]];\n\t\t\tNSArray* seperatedIconSuffix = [iconSuffix componentsSeparatedByString:@\"@\"];\n\n\t\t\tNSString* currentIconResolution = seperatedIconSuffix.firstObject;\n\t\t\tNSString* currentIconScale;\n\t\t\tif(seperatedIconSuffix.count > 1)\n\t\t\t{\n\t\t\t\tcurrentIconScale = seperatedIconSuffix.lastObject;\n\t\t\t}\n\n\t\t\tNSNumberFormatter* f = [[NSNumberFormatter alloc] init];\n\t\t\tf.numberStyle = NSNumberFormatterDecimalStyle;\n\n\t\t\tNSArray* separatedIconSize = [currentIconResolution componentsSeparatedByString:@\"x\"];\n\t\t\tNSNumber* widthNum = [f numberFromString:separatedIconSize.firstObject];\n\t\t\tNSNumber* heightNum = [f numberFromString:separatedIconSize.lastObject];\n\n\t\t\tCGSize iconSize = CGSizeMake(widthNum.unsignedIntegerValue, heightNum.unsignedIntegerValue);\n\n\t\t\tNSUInteger scale = 1;\n\t\t\tif(currentIconScale)\n\t\t\t{\n\t\t\t\tNSNumber* scaleNum = [f numberFromString:currentIconScale];\n\t\t\t\tscale = scaleNum.unsignedIntegerValue;\n\t\t\t}\n\n\t\t\tenumerateBlock(iconSize, scale, iconPath, stop);\n\t\t};\n\n\t\tif(_isArchive)\n\t\t{\n\t\t\tNSString* iconPrefix = [NSString stringWithFormat:@\"Payload/%@/%@\", _cachedAppBundleName, iconName];\n\t\t\t[self enumerateArchive:^(struct archive_entry* entry, BOOL* stop)\n\t\t\t{\n\t\t\t\tNSString* currentSubpath = [NSString stringWithUTF8String:archive_entry_pathname(entry)];\n\t\t\t\tif([currentSubpath hasPrefix:iconPrefix])\n\t\t\t\t{\n\t\t\t\t\twrapperBlock(currentSubpath, stop);\n\t\t\t\t}\n\t\t\t}];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tNSArray<NSString*>* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:_path error:nil];\n\t\t\tfor(NSString* fileName in files)\n\t\t\t{\n\t\t\t\tif([fileName hasPrefix:iconName])\n\t\t\t\t{\n\t\t\t\t\tNSString* iconPath = [_path stringByAppendingPathComponent:fileName];\n\n\t\t\t\t\tBOOL stop = NO;\n\t\t\t\t\twrapperBlock(iconPath, &stop);\n\t\t\t\t\tif(stop) return;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n}\n\n- (UIImage*)iconForSize:(CGSize)size\n{\n\tif(size.width != size.height)\n\t{\n\t\t//not supported\n\t\treturn nil;\n\t}\n\n\t// Flow: Check if icon with the exact size exists\n\t// If not, take the next best one and scale it down\n\n\t//UIImage* imageToReturn;\n\t__block NSString* foundIconPath;\n\n\t// Attempt 1: Check for icon with exact size\n\t[self enumerateAvailableIcons:^(CGSize iconSize, NSUInteger iconScale, NSString* iconPath, BOOL* stop)\n\t{\n\t\tif(CGSizeEqualToSize(iconSize, size) && UIScreen.mainScreen.scale == iconScale)\n\t\t{\n\t\t\tfoundIconPath = iconPath;\n\t\t\t//imageToReturn = imageWithSize([UIImage imageWithContentsOfFile:iconPath], size);\n\t\t\t*stop = YES;\n\t\t}\n\t}];\n\n\tif(!foundIconPath)\n\t{\n\t\t// Attempt 2: Check for icon with bigger size\n\t\t__block CGSize closestIconSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);\n\n\t\t[self enumerateAvailableIcons:^(CGSize iconSize, NSUInteger iconScale, NSString* iconPath, BOOL* stop)\n\t\t{\n\t\t\tif(iconSize.width > size.width && iconSize.width < closestIconSize.width)\n\t\t\t{\n\t\t\t\tclosestIconSize = iconSize;\n\t\t\t}\n\t\t}];\n\n\t\tif(closestIconSize.width == CGFLOAT_MAX)\n\t\t{\n\t\t\t// Attempt 3: Take biggest icon and scale it up\n\t\t\tclosestIconSize = CGSizeMake(0,0);\n\t\t\t[self enumerateAvailableIcons:^(CGSize iconSize, NSUInteger iconScale, NSString* iconPath, BOOL* stop)\n\t\t\t{\n\t\t\t\tif(iconSize.width > closestIconSize.width)\n\t\t\t\t{\n\t\t\t\t\tclosestIconSize = iconSize;\n\t\t\t\t}\n\t\t\t}];\n\t\t}\n\n\t\tif(closestIconSize.width == 0) return nil;\n\n\t\t[self enumerateAvailableIcons:^(CGSize iconSize, NSUInteger iconScale, NSString* iconPath, BOOL* stop)\n\t\t{\n\t\t\tif(CGSizeEqualToSize(iconSize, closestIconSize))\n\t\t\t{\n\t\t\t\tclosestIconSize = iconSize;\n\t\t\t\tfoundIconPath = iconPath;\n\t\t\t\t*stop = YES;\n\t\t\t}\n\t\t}];\n\t}\n\n\tif(!foundIconPath) return nil;\n\n\tif(_isArchive)\n\t{\n\t\t__block NSData* iconData;\n\n\t\tstruct archive_entry* iconEntry = [self archiveEntryForSubpath:foundIconPath];\n\t\tif(iconEntry)\n\t\t{\n\t\t\tsize_t size = archive_entry_size(iconEntry);\n\t\t\tvoid* buf = malloc(size);\n\t\t\tsize_t read = archive_read_data(_archive, buf, size);\n\t\t\t\n\t\t\tif(read == size)\n\t\t\t{\n\t\t\t\ticonData = [NSData dataWithBytes:buf length:size];\n\t\t\t}\n\n\t\t\tfree(buf);\n\t\t}\n\n\t\tif(iconData)\n\t\t{\n\t\t\treturn imageWithSize([UIImage imageWithData:iconData], size);\n\t\t}\n\t}\n\telse\n\t{\n\t\treturn imageWithSize([UIImage imageWithContentsOfFile:foundIconPath], size);\n\t}\n\treturn nil;\n}\n\n- (NSString*)displayName\n{\n\tNSString* displayName = _cachedInfoDictionary[@\"CFBundleDisplayName\"];\n\tif(!displayName || ![displayName isKindOfClass:NSString.class])\n\t{\n\t\tdisplayName = _cachedInfoDictionary[@\"CFBundleName\"];\n\t\tif(!displayName || ![displayName isKindOfClass:NSString.class])\n\t\t{\n\t\t\tdisplayName = _cachedInfoDictionary[@\"CFBundleExecutable\"];\n\t\t\tif(!displayName || ![displayName isKindOfClass:NSString.class])\n\t\t\t{\n\t\t\t\tif(_isArchive)\n\t\t\t\t{\n\t\t\t\t\tdisplayName = [_cachedAppBundleName stringByDeletingPathExtension];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdisplayName = [[_path lastPathComponent] stringByDeletingPathExtension];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn displayName;\n}\n\n- (NSString*)bundleIdentifier\n{\n\treturn _cachedInfoDictionary[@\"CFBundleIdentifier\"];\n}\n\n- (NSString*)versionString\n{\n\tNSString* version = _cachedInfoDictionary[@\"CFBundleShortVersionString\"];\n\tif(!version)\n\t{\n\t\tversion = _cachedInfoDictionary[@\"CFBundleVersion\"];\n\t}\n\treturn version;\n}\n\n- (NSString*)sizeString\n{\n\treturn [NSByteCountFormatter stringFromByteCount:_cachedSize countStyle:NSByteCountFormatterCountStyleFile];\n}\n\n- (NSString*)bundlePath\n{\n\tif(!_isArchive)\n\t{\n\t\treturn _path;\n\t}\n\treturn nil;\n}\n\n- (NSString*)registrationState\n{\n\treturn _cachedRegistrationState;\n}\n\n- (NSAttributedString*)detailedInfoTitle\n{\n\tNSString* displayName = [self displayName];\n\n\tNSMutableDictionary* titleAttributes = @{\n\t\tNSFontAttributeName : [UIFont boldSystemFontOfSize:16]\n\t}.mutableCopy;\n\tNSMutableAttributedString* description = [NSMutableAttributedString new];\n\n\tif(_cachedPreviewIcon)\n\t{\n\t\ttitleAttributes[NSBaselineOffsetAttributeName] = @9.0;\n\n\t\tNSTextAttachment* previewAttachment = [[NSTextAttachment alloc] init];\n\t\tpreviewAttachment.image = _cachedPreviewIcon;\n\t\t\n\t\t[description appendAttributedString:[NSAttributedString attributedStringWithAttachment:previewAttachment]];\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\" \" attributes:titleAttributes]];\n\t}\n\n\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:displayName attributes:titleAttributes]];\n\t\n\treturn description.copy;\n}\n\n- (NSAttributedString*)detailedInfoDescription\n{\n\tNSString* bundleId = [self bundleIdentifier];\n\tNSString* version = [self versionString];\n\tNSString* sizeString = [self sizeString];\n\t\n\t// Check if any bundles main binary runs unsandboxed\n\t__block BOOL isUnsandboxed = NO;\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\tif([key isEqualToString:@\"com.apple.private.security.container-required\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif(valueNum && [valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\tisUnsandboxed = !valueNum.boolValue;\n\t\t\t\tif(isUnsandboxed) *stop = YES;\n\t\t\t}\n\t\t} else if([key isEqualToString:@\"com.apple.private.security.no-container\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif(valueNum && [valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\tisUnsandboxed = valueNum.boolValue;\n\t\t\t\tif(isUnsandboxed) *stop = YES;\n\t\t\t}\n\t\t} else if([key isEqualToString:@\"com.apple.private.security.no-sandbox\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif(valueNum && [valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\tisUnsandboxed = valueNum.boolValue;\n\t\t\t\tif(isUnsandboxed) *stop = YES;\n\t\t\t}\n\t\t}\n\t}];\n\t\n\t// Check if any bundles main binary can spawn an external binary\n\t__block BOOL isPlatformApplication = NO;\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop)\n\t{\n\t\tif([key isEqualToString:@\"platform-application\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif(valueNum && [valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\tisPlatformApplication = valueNum.boolValue;\n\t\t\t\tif(isPlatformApplication) *stop = YES;\n\t\t\t}\n\t\t}\n\t}];\n\t\n\t// Check if any bundles main binary can spawn an external binary as root\n\t__block BOOL hasPersonaMngmt = NO;\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop)\n\t{\n\t\tif([key isEqualToString:@\"com.apple.private.persona-mgmt\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif(valueNum && [valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\thasPersonaMngmt = valueNum.boolValue;\n\t\t\t\tif(hasPersonaMngmt) *stop = YES;\n\t\t\t}\n\t\t}\n\t}];\n\t\n\t// Accessible containers\n\t// com.apple.developer.icloud-container-identifiers\n\t// com.apple.security.application-groups\n\t// Unrestricted if special entitlement\n\t\n\t__block BOOL unrestrictedContainerAccess = NO;\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop)\n\t{\n\t\tif([key isEqualToString:@\"com.apple.private.security.storage.AppDataContainers\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif(valueNum && [valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\tunrestrictedContainerAccess = valueNum.boolValue;\n\t\t\t\tif(hasPersonaMngmt) *stop = YES;\n\t\t\t}\n\t\t}\n\t}];\n\t\n\t__block NSMutableArray* accessibleContainers = [NSMutableArray new]; //array by design, should be ordered\n\tif(!unrestrictedContainerAccess)\n\t{\n\t\t__block NSString *dataContainer = nil;\n\n\t\t// If com.apple.private.security.container-required Entitlement is a string, prefer it to CFBundleIdentifier\n\t\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\t\tif([key isEqualToString:@\"com.apple.private.security.container-required\"])\n\t\t\t{\n\t\t\t\tNSString* valueString = (NSString*)value;\n\t\t\t\tif(valueString && [valueString isKindOfClass:NSString.class])\n\t\t\t\t{\n\t\t\t\t\tdataContainer = valueString;\n\t\t\t\t}\n\t\t\t}\n\t\t}];\n\n\t\t// Else take CFBundleIdentifier\n\t\tif (!dataContainer) {\n\t\t\t[self enumerateAllInfoDictionaries:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\t\t\tif([key isEqualToString:@\"CFBundleIdentifier\"])\n\t\t\t\t{\n\t\t\t\t\tNSString* valueStr = (NSString*)value;\n\t\t\t\t\tif([valueStr isKindOfClass:NSString.class])\n\t\t\t\t\t{\n\t\t\t\t\t\tdataContainer = valueStr;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}];\n\t\t}\n\n\t\tif (dataContainer) {\n\t\t\t[accessibleContainers addObject:dataContainer];\n\t\t}\n\n\t\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop)\n\t\t{\n\t\t\tif([key isEqualToString:@\"com.apple.developer.icloud-container-identifiers\"] || [key isEqualToString:@\"com.apple.security.application-groups\"] || [key isEqualToString:@\"com.apple.security.system-groups\"])\n\t\t\t{\n\t\t\t\tNSArray* valueArr = (NSArray*)value;\n\t\t\t\tif([valueArr isKindOfClass:NSArray.class])\n\t\t\t\t{\n\t\t\t\t\tfor(NSString* containerID in valueArr)\n\t\t\t\t\t{\n\t\t\t\t\t\tif([containerID isKindOfClass:NSString.class])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(![accessibleContainers containsObject:containerID])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t[accessibleContainers addObject:containerID];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}];\n\t}\n\n\t// Accessible Keychain Groups\n\t// keychain-access-groups\n\t// Unrestricted if single * (maybe?)\n\t__block BOOL unrestrictedKeychainAccess = NO;\n\t__block NSMutableSet* accessibleKeychainGroups = [NSMutableSet new];\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\tif([key isEqualToString:@\"keychain-access-groups\"])\n\t\t{\n\t\t\tNSArray* valueArr = (NSArray*)value;\n\t\t\tif([valueArr isKindOfClass:NSArray.class])\n\t\t\t{\n\t\t\t\tfor(NSString* keychainID in valueArr)\n\t\t\t\t{\n\t\t\t\t\tif([keychainID isKindOfClass:NSString.class])\n\t\t\t\t\t{\n\t\t\t\t\t\tif([keychainID isEqualToString:@\"*\"])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tunrestrictedKeychainAccess = YES;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[accessibleKeychainGroups addObject:keychainID];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}];\n\n\t__block NSMutableSet* URLSchemes = [NSMutableSet new];\n\t[self enumerateAllInfoDictionaries:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\tif([key isEqualToString:@\"CFBundleURLTypes\"])\n\t\t{\n\t\t\tNSArray* valueArr = (NSArray*)value;\n\t\t\tif([valueArr isKindOfClass:NSArray.class])\n\t\t\t{\n\t\t\t\tfor(NSDictionary* URLTypeDict in valueArr)\n\t\t\t\t{\n\t\t\t\t\tif([URLTypeDict isKindOfClass:NSDictionary.class])\n\t\t\t\t\t{\n\t\t\t\t\t\tNSArray* cURLSchemes = URLTypeDict[@\"CFBundleURLSchemes\"];\n\t\t\t\t\t\tif(cURLSchemes && [cURLSchemes isKindOfClass:NSArray.class])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor(NSString* URLScheme in cURLSchemes)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t[URLSchemes addObject:URLScheme];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}];\n\t\n\t__block NSMutableSet* allowedTccServices = [NSMutableSet new];\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\tif([key isEqualToString:@\"com.apple.private.tcc.allow\"])\n\t\t{\n\t\t\tNSArray* valueArr = (NSArray*)value;\n\t\t\tif([valueArr isKindOfClass:NSArray.class])\n\t\t\t{\n\t\t\t\tfor(NSString* serviceID in valueArr)\n\t\t\t\t{\n\t\t\t\t\tif([serviceID isKindOfClass:NSString.class])\n\t\t\t\t\t{\n\t\t\t\t\t\tNSString* displayName = commonTCCServices[serviceID];\n\t\t\t\t\t\tif(displayName == nil)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[allowedTccServices addObject:[serviceID stringByReplacingOccurrencesOfString:@\"kTCCService\" withString:@\"\"]];\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[allowedTccServices addObject:displayName];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if ([key isEqualToString:@\"com.apple.locationd.preauthorized\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif([valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\tif([valueNum boolValue])\n\t\t\t\t{\n\t\t\t\t\t[allowedTccServices addObject:@\"Location\"];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}];\n\n\t__block NSMutableSet* allowedMGKeys = [NSMutableSet new];\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\tif([key isEqualToString:@\"com.apple.private.MobileGestalt.AllowedProtectedKeys\"])\n\t\t{\n\t\t\tNSArray* valueArr = (NSArray*)value;\n\t\t\tif([valueArr isKindOfClass:NSArray.class])\n\t\t\t{\n\t\t\t\tfor(NSString* protectedKey in valueArr)\n\t\t\t\t{\n\t\t\t\t\tif([protectedKey isKindOfClass:NSString.class])\n\t\t\t\t\t{\n\t\t\t\t\t\t[allowedMGKeys addObject:protectedKey];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}];\n\n\tNSMutableParagraphStyle* leftAlignment = [[NSMutableParagraphStyle alloc] init];\n\tleftAlignment.alignment = NSTextAlignmentLeft;\n\n\tUIColor* dangerColor = [UIColor colorWithDynamicProvider:^UIColor*(UITraitCollection *traitCollection)\n\t{\n\t\tif(traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)\n\t\t{\n\t\t\treturn [UIColor orangeColor];\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn [UIColor redColor];\n\t\t}\n\t}];\n\n\tUIColor* warningColor = [UIColor colorWithDynamicProvider:^UIColor*(UITraitCollection *traitCollection)\n\t{\n\t\tif(traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)\n\t\t{\n\t\t\treturn [UIColor yellowColor];\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn [UIColor orangeColor];\n\t\t}\n\t}];\n\n\tNSMutableAttributedString* description = [NSMutableAttributedString new];\n\t\n\tNSDictionary* headerAttributes = @{\n\t\tNSFontAttributeName : [UIFont boldSystemFontOfSize:14],\n\t\tNSParagraphStyleAttributeName : leftAlignment\n\t};\n\n\tNSDictionary* bodyAttributes = @{\n\t\tNSFontAttributeName : [UIFont systemFontOfSize:11],\n\t\tNSParagraphStyleAttributeName : leftAlignment\n\t};\n\n\tNSDictionary* bodyWarningAttributes = @{\n\t\tNSFontAttributeName : [UIFont systemFontOfSize:11],\n\t\tNSParagraphStyleAttributeName : leftAlignment,\n\t\tNSForegroundColorAttributeName : warningColor\n\t};\n\n\tNSDictionary* bodyDangerAttributes = @{\n\t\tNSFontAttributeName : [UIFont systemFontOfSize:11],\n\t\tNSParagraphStyleAttributeName : leftAlignment,\n\t\tNSForegroundColorAttributeName : dangerColor\n\t};\n\n\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"Metadata\" attributes:headerAttributes]];\n\t\n\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@\"\\nBundle Identifier: %@\", bundleId] attributes:bodyAttributes]];\n\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@\"\\nVersion: %@\", version] attributes:bodyAttributes]];\n\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@\"\\nSize: %@\", sizeString] attributes:bodyAttributes]];\n\t\n\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\n\\nSandboxing\" attributes:headerAttributes]];\n\tif(isUnsandboxed)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nThe app runs unsandboxed and can access most of the file system.\" attributes:bodyWarningAttributes]];\n\t}\n\telse\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nThe app runs sandboxed and can only access the containers listed below.\" attributes:bodyAttributes]];\n\t}\n\n\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\n\\nCapabilities\" attributes:headerAttributes]];\n\tif(isPlatformApplication && isUnsandboxed && hasPersonaMngmt)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nThe app can spawn its own embedded binaries with root privileges.\" attributes:bodyDangerAttributes]];\n\t}\n\telse if(isPlatformApplication && isUnsandboxed)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nThe app can spawn arbitrary binaries as the mobile user.\" attributes:bodyWarningAttributes]];\n\t}\n\telse\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nThe app can not spawn other binaries.\" attributes:bodyAttributes]];\n\t}\n\n\tif(allowedTccServices.count)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\n\\nPrivacy\" attributes:headerAttributes]];\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nThe app can access the following services without asking for permission:\\n\" attributes:bodyWarningAttributes]];\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSListFormatter localizedStringByJoiningStrings:[allowedTccServices allObjects]] attributes:bodyAttributes]];\n\t}\n\t\n\tif (allowedMGKeys.count)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\n\\nDevice Info\" attributes:headerAttributes]];\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nThe app can access protected information about this device:\\n\" attributes:bodyWarningAttributes]];\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSListFormatter localizedStringByJoiningStrings:[allowedMGKeys allObjects]] attributes:bodyAttributes]];\n\t}\n    \n\tif(unrestrictedContainerAccess || accessibleContainers.count)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\n\\nAccessible Containers\" attributes:headerAttributes]];\n\t\tif(unrestrictedContainerAccess)\n\t\t{\n\t\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nUnrestricted, the app can access all data containers on the system.\" attributes:bodyDangerAttributes]];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(NSString* containerID in accessibleContainers)\n\t\t\t{\n\t\t\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@\"\\n%@\", containerID] attributes:bodyAttributes]];\n\t\t\t}\n\t\t}\n\t}\n\n\tif(unrestrictedKeychainAccess || accessibleKeychainGroups.count)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\n\\nAccessible Keychain Groups\" attributes:headerAttributes]];\n\t\tif(unrestrictedKeychainAccess)\n\t\t{\n\t\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\nUnrestricted, the app can access the entire keychain.\" attributes:bodyDangerAttributes]];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(NSString* keychainID in accessibleKeychainGroups)\n\t\t\t{\n\t\t\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@\"\\n%@\", keychainID] attributes:bodyAttributes]];\n\t\t\t}\n\t\t}\n\t}\n\n\tif(URLSchemes.count)\n\t{\n\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:@\"\\n\\nURL Schemes\" attributes:headerAttributes]];\n\n\t\tfor(NSString* URLScheme in URLSchemes)\n\t\t{\n\t\t\t[description appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@\"\\n%@\", URLScheme] attributes:bodyAttributes]];\n\t\t}\n\t}\n\n\treturn description;\n}\n\n- (void)log\n{\n\tNSLog(@\"entitlements:\");\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\tNSLog(@\"%@ -> %@\", key, value);\n\t}];\n\t\n\tNSLog(@\"info dictionaries:\");\n\t[self enumerateAllInfoDictionaries:^(NSString *key, NSObject *value, BOOL *stop) {\n\t\tNSLog(@\"%@ -> %@\", key, value);\n\t}];\n}\n\n- (BOOL)isDebuggable\n{\n\t[self loadEntitlements];\n\t__block BOOL debuggable = NO;\n\t[self enumerateAllEntitlements:^(NSString *key, NSObject *value, BOOL *stop)\n\t{\n\t\tif([key isEqualToString:@\"get-task-allow\"])\n\t\t{\n\t\t\tNSNumber* valueNum = (NSNumber*)value;\n\t\t\tif(valueNum && [valueNum isKindOfClass:NSNumber.class])\n\t\t\t{\n\t\t\t\tdebuggable = valueNum.boolValue;\n\t\t\t\t*stop = YES;\n\t\t\t}\n\t\t}\n\t}];\n\treturn debuggable;\n}\n\n@end\n"
  },
  {
    "path": "TrollStore/TSAppTableViewController.h",
    "content": "#import <UIKit/UIKit.h>\n#import \"TSAppInfo.h\"\n#import <CoreServices.h>\n\n@interface TSAppTableViewController : UITableViewController <UISearchResultsUpdating, UIDocumentPickerDelegate, LSApplicationWorkspaceObserverProtocol>\n{\n    UIImage* _placeholderIcon;\n    NSArray<TSAppInfo*>* _cachedAppInfos;\n    NSMutableDictionary* _cachedIcons;\n    UISearchController* _searchController;\n\tNSString* _searchKey;\n}\n\n@end"
  },
  {
    "path": "TrollStore/TSAppTableViewController.m",
    "content": "#import \"TSAppTableViewController.h\"\n\n#import \"TSApplicationsManager.h\"\n#import <TSPresentationDelegate.h>\n#import \"TSInstallationController.h\"\n#import \"TSUtil.h\"\n@import UniformTypeIdentifiers;\n\n#define ICON_FORMAT_IPAD 8\n#define ICON_FORMAT_IPHONE 10\n\nNSInteger iconFormatToUse(void)\n{\n\tif(UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)\n\t{\n\t\treturn ICON_FORMAT_IPAD;\n\t}\n\telse\n\t{\n\t\treturn ICON_FORMAT_IPHONE;\n\t}\n}\n\nUIImage* imageWithSize(UIImage* image, CGSize size)\n{\n\tif(CGSizeEqualToSize(image.size, size)) return image;\n\tUIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);\n\tCGRect imageRect = CGRectMake(0.0, 0.0, size.width, size.height);\n\t[image drawInRect:imageRect];\n\tUIImage* outImage = UIGraphicsGetImageFromCurrentImageContext();\n\tUIGraphicsEndImageContext();\n\treturn outImage;\n}\n\n@interface UIImage ()\n+ (UIImage *)_applicationIconImageForBundleIdentifier:(NSString *)id format:(NSInteger)format scale:(double)scale;\n@end\n\n@implementation TSAppTableViewController\n\n- (void)loadAppInfos\n{\n\tNSArray* appPaths = [[TSApplicationsManager sharedInstance] installedAppPaths];\n\tNSMutableArray<TSAppInfo*>* appInfos = [NSMutableArray new];\n\n\tfor(NSString* appPath in appPaths)\n\t{\n\t\tTSAppInfo* appInfo = [[TSAppInfo alloc] initWithAppBundlePath:appPath];\n\t\t[appInfo sync_loadBasicInfo];\n\t\t[appInfos addObject:appInfo];\n\t}\n\n\tif(_searchKey && ![_searchKey isEqualToString:@\"\"])\n\t{\n\t\t[appInfos enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(TSAppInfo* appInfo, NSUInteger idx, BOOL* stop)\n\t\t{\n\t\t\tNSString* appName = [appInfo displayName];\n\t\t\tBOOL nameMatch = [appName rangeOfString:_searchKey options:NSCaseInsensitiveSearch range:NSMakeRange(0, [appName length]) locale:[NSLocale currentLocale]].location != NSNotFound;\n\t\t\tif(!nameMatch)\n\t\t\t{\n\t\t\t\t[appInfos removeObjectAtIndex:idx];\n\t\t\t}\n\t\t}];\n\t}\n\n\t[appInfos sortUsingComparator:^(TSAppInfo* appInfoA, TSAppInfo* appInfoB)\n\t{\n\t\treturn [[appInfoA displayName] localizedStandardCompare:[appInfoB displayName]];\n\t}];\n\n\t_cachedAppInfos = appInfos.copy;\n}\n\n- (instancetype)init\n{\n\tself = [super init];\n\tif(self)\n\t{\n\t\t[self loadAppInfos];\n\t\t_placeholderIcon = [UIImage _applicationIconImageForBundleIdentifier:@\"com.apple.WebSheet\" format:iconFormatToUse() scale:[UIScreen mainScreen].scale];\n\t\t_cachedIcons = [NSMutableDictionary new];\n\t\t[[LSApplicationWorkspace defaultWorkspace] addObserver:self];\n\t}\n\treturn self;\n}\n\n- (void)dealloc\n{\n\t[[LSApplicationWorkspace defaultWorkspace] removeObserver:self];\n}\n\n- (void)reloadTable\n{\n\t[self loadAppInfos];\n\tdispatch_async(dispatch_get_main_queue(), ^\n\t{\n\t\t[self.tableView reloadData];\n\t});\n}\n\n- (void)loadView\n{\n\t[super loadView];\n\t[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadTable) name:@\"ApplicationsChanged\" object:nil];\n}\n\n- (void)viewDidLoad\n{\n\t[super viewDidLoad];\n\t\n\tself.tableView.allowsMultipleSelectionDuringEditing = NO;\n\tself.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];\n\n\t[self _setUpNavigationBar];\n\t[self _setUpSearchBar];\n}\n\n- (void)_setUpNavigationBar\n{\n\tUIAction* installFromFileAction = [UIAction actionWithTitle:@\"Install IPA File\" image:[UIImage systemImageNamed:@\"doc.badge.plus\"] identifier:@\"InstallIPAFile\" handler:^(__kindof UIAction *action)\n\t{\n\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t{\n\t\t\tUTType* ipaType = [UTType typeWithFilenameExtension:@\"ipa\" conformingToType:UTTypeData];\n\t\t\tUTType* tipaType = [UTType typeWithFilenameExtension:@\"tipa\" conformingToType:UTTypeData];\n\n\t\t\tUIDocumentPickerViewController* documentPickerVC = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:@[ipaType, tipaType]];\n\t\t\tdocumentPickerVC.allowsMultipleSelection = NO;\n\t\t\tdocumentPickerVC.delegate = self;\n\n\t\t\t[TSPresentationDelegate presentViewController:documentPickerVC animated:YES completion:nil];\n\t\t});\n\t}];\n\n\tUIAction* installFromURLAction = [UIAction actionWithTitle:@\"Install from URL\" image:[UIImage systemImageNamed:@\"link.badge.plus\"] identifier:@\"InstallFromURL\" handler:^(__kindof UIAction *action)\n\t{\n\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t{\n\t\t\tUIAlertController* installURLController = [UIAlertController alertControllerWithTitle:@\"Install from URL\" message:@\"\" preferredStyle:UIAlertControllerStyleAlert];\n\n\t\t\t[installURLController addTextFieldWithConfigurationHandler:^(UITextField *textField) {\n\t\t\t\ttextField.placeholder = @\"URL\";\n\t\t\t}];\n\n\t\t\tUIAlertAction* installAction = [UIAlertAction actionWithTitle:@\"Install\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t\t{\n\t\t\t\tNSString* URLString = installURLController.textFields.firstObject.text;\n\t\t\t\tNSURL* remoteURL = [NSURL URLWithString:URLString];\n\n\t\t\t\t[TSInstallationController handleAppInstallFromRemoteURL:remoteURL completion:nil];\n\t\t\t}];\n\t\t\t[installURLController addAction:installAction];\n\n\t\t\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\t\t\t[installURLController addAction:cancelAction];\n\n\t\t\t[TSPresentationDelegate presentViewController:installURLController animated:YES completion:nil];\n\t\t});\n\t}];\n\n\tUIMenu* installMenu = [UIMenu menuWithChildren:@[installFromFileAction, installFromURLAction]];\n\n\tUIBarButtonItem* installBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@\"plus\"] menu:installMenu];\n\t\n\tself.navigationItem.rightBarButtonItems = @[installBarButtonItem];\n}\n\n- (void)_setUpSearchBar\n{\n\t_searchController = [[UISearchController alloc] initWithSearchResultsController:nil];\n\t_searchController.searchResultsUpdater = self;\n\t_searchController.obscuresBackgroundDuringPresentation = NO;\n\tself.navigationItem.searchController = _searchController;\n\tself.navigationItem.hidesSearchBarWhenScrolling = YES;\n}\n\n- (void)updateSearchResultsForSearchController:(UISearchController *)searchController\n{\n\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n\t\t_searchKey = searchController.searchBar.text;\n\t\t[self reloadTable];\n\t});\n}\n\n- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls\n{\n\tNSString* pathToIPA = urls.firstObject.path;\n\t[TSInstallationController presentInstallationAlertIfEnabledForFile:pathToIPA isRemoteInstall:NO completion:nil];\n}\n\n- (void)openAppPressedForRowAtIndexPath:(NSIndexPath*)indexPath enableJIT:(BOOL)enableJIT\n{\n\tTSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];\n\n\tTSAppInfo* appInfo = _cachedAppInfos[indexPath.row];\n\tNSString* appId = [appInfo bundleIdentifier];\n\tBOOL didOpen = [appsManager openApplicationWithBundleID:appId];\n\n\t// if we failed to open the app, show an alert\n\tif(!didOpen)\n\t{\n\t\tNSString* failMessage = @\"\";\n\t\tif([[appInfo registrationState] isEqualToString:@\"User\"])\n\t\t{\n\t\t\tfailMessage = @\"This app was not able to launch because it has a \\\"User\\\" registration state, register it as \\\"System\\\" and try again.\";\n\t\t}\n\n\t\tNSString* failTitle = [NSString stringWithFormat:@\"Failed to open %@\", appId];\n\t\tUIAlertController* didFailController = [UIAlertController alertControllerWithTitle:failTitle message:failMessage preferredStyle:UIAlertControllerStyleAlert];\n\t\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\n\t\t[didFailController addAction:cancelAction];\n\t\t[TSPresentationDelegate presentViewController:didFailController animated:YES completion:nil];\n\t}\n\telse if (enableJIT)\n\t{\n\t\tint ret = [appsManager enableJITForBundleID:appId];\n\t\tif (ret != 0)\n\t\t{\n\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@\"Error\" message:[NSString stringWithFormat:@\"Error enabling JIT: trollstorehelper returned %d\", ret] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t[errorAlert addAction:closeAction];\n\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t}\n\t}\n}\n\n- (void)showDetailsPressedForRowAtIndexPath:(NSIndexPath*)indexPath\n{\n\tTSAppInfo* appInfo = _cachedAppInfos[indexPath.row];\n\n\t[appInfo loadInfoWithCompletion:^(NSError* error)\n\t{\n\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t{\n\t\t\tif(!error)\n\t\t\t{\n\t\t\t\tUIAlertController* detailsAlert = [UIAlertController alertControllerWithTitle:@\"\" message:@\"\" preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\tdetailsAlert.attributedTitle = [appInfo detailedInfoTitle];\n\t\t\t\tdetailsAlert.attributedMessage = [appInfo detailedInfoDescription];\n\n\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t[detailsAlert addAction:closeAction];\n\n\t\t\t\t[TSPresentationDelegate presentViewController:detailsAlert animated:YES completion:nil];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@\"Parse Error %ld\", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t\t}\n\t\t});\n\t}];\n}\n\n- (void)changeAppRegistrationForRowAtIndexPath:(NSIndexPath*)indexPath toState:(NSString*)newState\n{\n\tTSAppInfo* appInfo = _cachedAppInfos[indexPath.row];\n\n\tif([newState isEqualToString:@\"User\"])\n\t{\n\t\tNSString* title = [NSString stringWithFormat:@\"Switching '%@' to \\\"User\\\" Registration\", [appInfo displayName]];\n\t\tUIAlertController* confirmationAlert = [UIAlertController alertControllerWithTitle:title message:@\"Switching this app to a \\\"User\\\" registration will make it unlaunchable after the next respring because the bugs exploited in TrollStore only affect apps registered as \\\"System\\\".\\nThe purpose of this option is to make the app temporarily show up in settings, so you can adjust the settings and then switch it back to a \\\"System\\\" registration (TrollStore installed apps do not show up in settings otherwise). Additionally, the \\\"User\\\" registration state is also useful to temporarily fix iTunes file sharing, which also doesn't work for TrollStore installed apps otherwise.\\nWhen you're done making the changes you need and want the app to become launchable again, you will need to switch it back to \\\"System\\\" state in TrollStore.\" preferredStyle:UIAlertControllerStyleAlert];\n\n\t\tUIAlertAction* switchToUserAction = [UIAlertAction actionWithTitle:@\"Switch to \\\"User\\\"\" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)\n\t\t{\n\t\t\t[[TSApplicationsManager sharedInstance] changeAppRegistration:[appInfo bundlePath] toState:newState];\n\t\t\t[appInfo sync_loadBasicInfo];\n\t\t}];\n\n\t\t[confirmationAlert addAction:switchToUserAction];\n\n\t\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\n\t\t[confirmationAlert addAction:cancelAction];\n\n\t\t[TSPresentationDelegate presentViewController:confirmationAlert animated:YES completion:nil];\n\t}\n\telse\n\t{\n\t\t[[TSApplicationsManager sharedInstance] changeAppRegistration:[appInfo bundlePath] toState:newState];\n\t\t[appInfo sync_loadBasicInfo];\n\n\t\tNSString* title = [NSString stringWithFormat:@\"Switched '%@' to \\\"System\\\" Registration\", [appInfo displayName]];\n\n\t\tUIAlertController* infoAlert = [UIAlertController alertControllerWithTitle:title message:@\"The app has been switched to the \\\"System\\\" registration state and will become launchable again after a respring.\" preferredStyle:UIAlertControllerStyleAlert];\n\n\t\tUIAlertAction* respringAction = [UIAlertAction actionWithTitle:@\"Respring\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t{\n\t\t\trespring();\n\t\t}];\n\n\t\t[infoAlert addAction:respringAction];\n\n\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\n\t\t[infoAlert addAction:closeAction];\n\n\t\t[TSPresentationDelegate presentViewController:infoAlert animated:YES completion:nil];\n\t}\n}\n\n- (void)uninstallPressedForRowAtIndexPath:(NSIndexPath*)indexPath\n{\n\tTSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];\n\n\tTSAppInfo* appInfo = _cachedAppInfos[indexPath.row];\n\n\tNSString* appPath = [appInfo bundlePath];\n\tNSString* appId = [appInfo bundleIdentifier];\n\tNSString* appName = [appInfo displayName];\n\n\tUIAlertController* confirmAlert = [UIAlertController alertControllerWithTitle:@\"Confirm Uninstallation\" message:[NSString stringWithFormat:@\"Uninstalling the app '%@' will delete the app and all data associated to it.\", appName] preferredStyle:UIAlertControllerStyleAlert];\n\n\tUIAlertAction* uninstallAction = [UIAlertAction actionWithTitle:@\"Uninstall\" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)\n\t{\n\t\tif(appId)\n\t\t{\n\t\t\t[appsManager uninstallApp:appId];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t[appsManager uninstallAppByPath:appPath];\n\t\t}\n\t}];\n\t[confirmAlert addAction:uninstallAction];\n\n\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\t[confirmAlert addAction:cancelAction];\n\n\t[TSPresentationDelegate presentViewController:confirmAlert animated:YES completion:nil];\n}\n\n- (void)deselectRow\n{\n\t[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];\n}\n\n#pragma mark - Table view data source\n\n- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView\n{\n\treturn 1;\n}\n\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section\n{\n\treturn _cachedAppInfos.count;\n}\n\n- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection\n{\n\t[self reloadTable];\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath\n{\n\tUITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@\"ApplicationCell\"];\n\tif(!cell) {\n\t\tcell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@\"ApplicationCell\"];\n\t}\n\n\tif(!indexPath || indexPath.row > (_cachedAppInfos.count - 1)) return cell;\n\n\tTSAppInfo* appInfo = _cachedAppInfos[indexPath.row];\n\tNSString* appId = [appInfo bundleIdentifier];\n\tNSString* appVersion = [appInfo versionString];\n\n\t// Configure the cell...\n\tcell.textLabel.text = [appInfo displayName];\n\tcell.detailTextLabel.text = [NSString stringWithFormat:@\"%@ • %@\", appVersion, appId];\n\tcell.imageView.layer.borderWidth = 1;\n\tcell.imageView.layer.borderColor = [UIColor.labelColor colorWithAlphaComponent:0.1].CGColor;\n\tcell.imageView.layer.cornerRadius = 13.5;\n\tcell.imageView.layer.masksToBounds = YES;\n\tcell.imageView.layer.cornerCurve = kCACornerCurveContinuous;\n\n\tif(appId)\n\t{\n\t\tUIImage* cachedIcon = _cachedIcons[appId];\n\t\tif(cachedIcon)\n\t\t{\n\t\t\tcell.imageView.image = cachedIcon;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcell.imageView.image = _placeholderIcon;\n\t\t\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^\n\t\t\t{\n\t\t\t\tUIImage* iconImage = imageWithSize([UIImage _applicationIconImageForBundleIdentifier:appId format:iconFormatToUse() scale:[UIScreen mainScreen].scale], _placeholderIcon.size);\n\t\t\t\t_cachedIcons[appId] = iconImage;\n\t\t\t\tdispatch_async(dispatch_get_main_queue(), ^{\n\t\t\t\t\tNSIndexPath *curIndexPath = [NSIndexPath indexPathForRow:[_cachedAppInfos indexOfObject:appInfo] inSection:0];\n\t\t\t\t\tUITableViewCell *curCell = [tableView cellForRowAtIndexPath:curIndexPath];\n\t\t\t\t\tif(curCell)\n\t\t\t\t\t{\n\t\t\t\t\t\tcurCell.imageView.image = iconImage;\n\t\t\t\t\t\t[curCell setNeedsLayout];\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\telse\n\t{\n\t\tcell.imageView.image = _placeholderIcon;\n\t}\n\n\tcell.preservesSuperviewLayoutMargins = NO;\n\tcell.separatorInset = UIEdgeInsetsZero;\n\tcell.layoutMargins = UIEdgeInsetsZero;\n\n\treturn cell;\n}\n\n- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath\n{\n\treturn 80.0f;\n}\n\n- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath\n{\n\tif(editingStyle == UITableViewCellEditingStyleDelete)\n\t{\n\t\t[self uninstallPressedForRowAtIndexPath:indexPath];\n\t}\n}\n\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath\n{\n\tTSAppInfo* appInfo = _cachedAppInfos[indexPath.row];\n\n\tNSString* appId = [appInfo bundleIdentifier];\n\tNSString* appName = [appInfo displayName];\n\n\tUIAlertController* appSelectAlert = [UIAlertController alertControllerWithTitle:appName?:@\"\" message:appId?:@\"\" preferredStyle:UIAlertControllerStyleActionSheet];\n\n\tUIAlertAction* openAction = [UIAlertAction actionWithTitle:@\"Open\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t{\n\t\t[self openAppPressedForRowAtIndexPath:indexPath enableJIT:NO];\n\t\t[self deselectRow];\n\t}];\n\t[appSelectAlert addAction:openAction];\n\n\tif ([appInfo isDebuggable])\n\t{\n\t\tUIAlertAction* openWithJITAction = [UIAlertAction actionWithTitle:@\"Open with JIT\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t{\n\t\t\t[self openAppPressedForRowAtIndexPath:indexPath enableJIT:YES];\n\t\t\t[self deselectRow];\n\t\t}];\n\t\t[appSelectAlert addAction:openWithJITAction];\n\t}\n\n\tUIAlertAction* showDetailsAction = [UIAlertAction actionWithTitle:@\"Show Details\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t{\n\t\t[self showDetailsPressedForRowAtIndexPath:indexPath];\n\t\t[self deselectRow];\n\t}];\n\t[appSelectAlert addAction:showDetailsAction];\n\n\tNSString* switchState;\n\tNSString* registrationState = [appInfo registrationState];\n\tUIAlertActionStyle switchActionStyle = 0;\n\tif([registrationState isEqualToString:@\"System\"])\n\t{\n\t\tswitchState = @\"User\";\n\t\tswitchActionStyle = UIAlertActionStyleDestructive;\n\t}\n\telse if([registrationState isEqualToString:@\"User\"])\n\t{\n\t\tswitchState = @\"System\";\n\t\tswitchActionStyle = UIAlertActionStyleDefault;\n\t}\n\n\tUIAlertAction* switchRegistrationAction = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@\"Switch to \\\"%@\\\" Registration\", switchState] style:switchActionStyle handler:^(UIAlertAction* action)\n\t{\n\t\t[self changeAppRegistrationForRowAtIndexPath:indexPath toState:switchState];\n\t\t[self deselectRow];\n\t}];\n\t[appSelectAlert addAction:switchRegistrationAction];\n\n\tUIAlertAction* uninstallAction = [UIAlertAction actionWithTitle:@\"Uninstall App\" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action)\n\t{\n\t\t[self uninstallPressedForRowAtIndexPath:indexPath];\n\t\t[self deselectRow];\n\t}];\n\t[appSelectAlert addAction:uninstallAction];\n\n\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)\n\t{\n\t\t[self deselectRow];\n\t}];\n\t[appSelectAlert addAction:cancelAction];\n\n\tappSelectAlert.popoverPresentationController.sourceView = tableView;\n\tappSelectAlert.popoverPresentationController.sourceRect = [tableView rectForRowAtIndexPath:indexPath];\n\n\t[TSPresentationDelegate presentViewController:appSelectAlert animated:YES completion:nil];\n}\n\n- (void)purgeCachedIconsForApps:(NSArray <LSApplicationProxy *>*)apps\n{\n\tfor (LSApplicationProxy *appProxy in apps) {\n\t\tNSString *appId = appProxy.bundleIdentifier;\n\t\tif (_cachedIcons[appId]) {\n\t\t\t[_cachedIcons removeObjectForKey:appId];\n\t\t}\n\t}\n}\n\n- (void)applicationsDidInstall:(NSArray <LSApplicationProxy *>*)apps\n{\n\t[self purgeCachedIconsForApps:apps];\n\t[self reloadTable];\n}\n\n- (void)applicationsDidUninstall:(NSArray <LSApplicationProxy *>*)apps\n{\n\t[self purgeCachedIconsForApps:apps];\n\t[self reloadTable];\n}\n\n@end\n"
  },
  {
    "path": "TrollStore/TSApplicationsManager.h",
    "content": "#import <Foundation/Foundation.h>\n\n#define TROLLSTORE_ROOT_PATH @\"/var/containers/Bundle/TrollStore\"\n#define TROLLSTORE_MAIN_PATH [TROLLSTORE_ROOT_PATH stringByAppendingPathComponent:@\"Main\"]\n#define TROLLSTORE_APPLICATIONS_PATH [TROLLSTORE_ROOT_PATH stringByAppendingPathComponent:@\"Applications\"]\n\n@interface TSApplicationsManager : NSObject\n\n+ (instancetype)sharedInstance;\n\n- (NSArray*)installedAppPaths;\n\n- (NSError*)errorForCode:(int)code;\n- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut;\n- (int)installIpa:(NSString*)pathToIpa;\n- (int)uninstallApp:(NSString*)appId;\n- (int)uninstallAppByPath:(NSString*)path;\n- (BOOL)openApplicationWithBundleID:(NSString *)appID;\n- (int)enableJITForBundleID:(NSString *)appID;\n- (int)changeAppRegistration:(NSString*)appPath toState:(NSString*)newState;\n\n@end"
  },
  {
    "path": "TrollStore/TSApplicationsManager.m",
    "content": "#import \"TSApplicationsManager.h\"\n#import <TSUtil.h>\nextern NSUserDefaults* trollStoreUserDefaults();\n\n@implementation TSApplicationsManager\n\n+ (instancetype)sharedInstance\n{\n    static TSApplicationsManager *sharedInstance = nil;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        sharedInstance = [[TSApplicationsManager alloc] init];\n    });\n    return sharedInstance;\n}\n\n- (NSArray*)installedAppPaths\n{\n    return trollStoreInstalledAppBundlePaths();\n}\n\n- (NSError*)errorForCode:(int)code\n{\n    NSString* errorDescription = @\"Unknown Error\";\n    switch(code)\n    {\n        // IPA install errors\n        case 166:\n        errorDescription = @\"The IPA file does not exist or is not accessible.\";\n        break;\n        case 167:\n        errorDescription = @\"The IPA file does not appear to contain an app.\";\n        break;\n        case 168:\n        errorDescription = @\"Failed to extract IPA file.\";\n        break;\n        case 169:\n        errorDescription = @\"Failed to extract update tar file.\";\n        break;\n        // App install errors\n        case 170:\n        errorDescription = @\"Failed to create container for app bundle.\";\n        break;\n        case 171:\n        errorDescription = @\"A non \"APP_NAME@\" or a \"OTHER_APP_NAME@\" app with the same identifier is already installed. If you are absolutely sure it is not, you can force install it.\";\n        break;\n        case 172:\n        errorDescription = @\"The app does not contain an Info.plist file.\";\n        break;\n        case 173:\n        errorDescription = @\"The app is not signed with a fake CoreTrust certificate and ldid is not installed. Install ldid in the settings tab and try again.\";\n        break;\n        case 174:\n        errorDescription = @\"The app's main executable does not exist.\";\n        break;\n        case 175: {\n            //if (@available(iOS 16, *)) {\n            //    errorDescription = @\"Failed to sign the app.\";\n            //}\n            //else {\n                errorDescription = @\"Failed to sign the app. ldid returned a non zero status code.\";\n            //}\n        }\n        break;\n        case 176:\n        errorDescription = @\"The app's Info.plist is missing required values.\";\n        break;\n        case 177:\n        errorDescription = @\"Failed to mark app as TrollStore app.\";\n        break;\n        case 178:\n        errorDescription = @\"Failed to copy app bundle.\";\n        break;\n        case 179:\n        errorDescription = @\"The app you tried to install has the same identifier as a system app already installed on the device. The installation has been prevented to protect you from possible bootloops or other issues.\";\n        break;\n        case 180:\n        errorDescription = @\"The app you tried to install has an encrypted main binary, which cannot have the CoreTrust bypass applied to it. Please ensure you install decrypted apps.\";\n        break;\n        case 181:\n        errorDescription = @\"Failed to add app to icon cache.\";\n        break;\n        case 182:\n        errorDescription = @\"The app was installed successfully, but requires developer mode to be enabled to run. After rebooting, select \\\"Turn On\\\" to enable developer mode.\";\n        break;\n        case 183:\n        errorDescription = @\"Failed to enable developer mode.\";\n        break;\n        case 184:\n        errorDescription = @\"The app was installed successfully, but has additional binaries that are encrypted (e.g. extensions, plugins). The app itself should work, but you may experience broken functionality as a result.\";\n        break;\n        case 185:\n        errorDescription = @\"Failed to sign the app. The CoreTrust bypass returned a non zero status code.\";\n    }\n\n    NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}];\n    return error;\n}\n\n- (int)installIpa:(NSString*)pathToIpa force:(BOOL)force log:(NSString**)logOut\n{\n    NSMutableArray* args = [NSMutableArray new];\n    [args addObject:@\"install\"];\n    if(force)\n    {\n        [args addObject:@\"force\"];\n    }\n    NSNumber* installationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@\"installationMethod\"];\n    int installationMethodToUse = installationMethodToUseNum ? installationMethodToUseNum.intValue : 1;\n    if(installationMethodToUse == 1)\n    {\n        [args addObject:@\"custom\"];\n    }\n    else\n    {\n        [args addObject:@\"installd\"];\n    }\n    [args addObject:pathToIpa];\n\n    int ret = spawnRoot(rootHelperPath(), args, nil, logOut);\n    [[NSNotificationCenter defaultCenter] postNotificationName:@\"ApplicationsChanged\" object:nil];\n    return ret;\n}\n\n- (int)installIpa:(NSString*)pathToIpa\n{\n    return [self installIpa:pathToIpa force:NO log:nil];\n}\n\n- (int)uninstallApp:(NSString*)appId\n{\n    if(!appId) return -200;\n\n    NSMutableArray* args = [NSMutableArray new];\n    [args addObject:@\"uninstall\"];\n\n    NSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@\"uninstallationMethod\"];\n    int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0;\n    if(uninstallationMethodToUse == 1)\n    {\n        [args addObject:@\"custom\"];\n    }\n    else\n    {\n        [args addObject:@\"installd\"];\n    }\n\n    [args addObject:appId];\n\n    int ret = spawnRoot(rootHelperPath(), args, nil, nil);\n    [[NSNotificationCenter defaultCenter] postNotificationName:@\"ApplicationsChanged\" object:nil];\n    return ret;\n}\n\n- (int)uninstallAppByPath:(NSString*)path\n{\n    if(!path) return -200;\n\n    NSMutableArray* args = [NSMutableArray new];\n    [args addObject:@\"uninstall-path\"];\n\n    NSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@\"uninstallationMethod\"];\n    int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0;\n    if(uninstallationMethodToUse == 1)\n    {\n        [args addObject:@\"custom\"];\n    }\n    else\n    {\n        [args addObject:@\"installd\"];\n    }\n\n    [args addObject:path];\n\n    int ret = spawnRoot(rootHelperPath(), args, nil, nil);\n    [[NSNotificationCenter defaultCenter] postNotificationName:@\"ApplicationsChanged\" object:nil];\n    return ret;\n}\n\n- (BOOL)openApplicationWithBundleID:(NSString *)appId\n{\n    return [[LSApplicationWorkspace defaultWorkspace] openApplicationWithBundleID:appId];\n}\n\n- (int)enableJITForBundleID:(NSString *)appId\n{\n    return spawnRoot(rootHelperPath(), @[@\"enable-jit\", appId], nil, nil);\n}\n\n- (int)changeAppRegistration:(NSString*)appPath toState:(NSString*)newState\n{\n    if(!appPath || !newState) return -200;\n    return spawnRoot(rootHelperPath(), @[@\"modify-registration\", appPath, newState], nil, nil);\n}\n\n@end"
  },
  {
    "path": "TrollStore/TSCommonTCCServiceNames.h",
    "content": "//\n//  TSCommonTCCServiceNames.h\n//  IPAInfo\n//\n//  Created by Luke Noble on 30.10.22.\n//\n\n#import <Foundation/Foundation.h>\n\nstatic NSDictionary* const commonTCCServices = @{\n    @\"kTCCServicePhotos\": @\"Photo Library\",\n    @\"kTCCServicePhotosAdd\": @\"Photo Library (Add)\",\n    @\"kTCCServiceCamera\": @\"Camera\",\n    @\"kTCCServiceMicrophone\": @\"Microphone\",\n    @\"kTCCServiceAddressBook\": @\"Contacts\",\n    @\"kTCCServiceCalendar\": @\"Calendars\",\n    @\"kTCCServiceReminders\": @\"Reminders\",\n    @\"kTCCServiceWillow\": @\"HomeKit\",\n    @\"kTCCServiceGameCenterFriends\": @\"Game Center Friends\",\n    @\"kTCCServiceExposureNotification\": @\"Exposure Notifications\",\n    @\"kTCCServiceFocusStatus\": @\"Focus Status\",\n    @\"kTCCServiceUserTracking\": @\"User Tracking\",\n    @\"kTCCServiceFaceID\": @\"Face ID\",\n    @\"kTCCServiceMediaLibrary\": @\"Apple Media Library\",\n    @\"kTCCServiceMotion\": @\"Motion Sensors\",\n    @\"kTCCServiceNearbyInteraction\": @\"Nearby Device Interaction\",\n    @\"kTCCServiceBluetoothAlways\": @\"Bluetooth (Always)\",\n    @\"kTCCServiceBluetoothWhileInUse\": @\"Bluetooth (While In Use)\",\n    @\"kTCCServiceBluetoothPeripheral\": @\"Bluetooth (Peripherals)\",\n    @\"kTCCServiceLocation\": @\"Location\"\n};\n"
  },
  {
    "path": "TrollStore/TSDonateListController.h",
    "content": "#import <Preferences/PSListController.h>\n\n@interface TSDonateListController : PSListController\n\n@end"
  },
  {
    "path": "TrollStore/TSDonateListController.m",
    "content": "#import \"TSDonateListController.h\"\n#import <Preferences/PSSpecifier.h>\n\n@implementation TSDonateListController\n\n\n- (void)donateToAlfiePressed\n{\n\t[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@\"https://ko-fi.com/alfiecg_dev\"] options:@{} completionHandler:^(BOOL success){}];\n}\n\n- (void)donateToOpaPressed\n{\n\t[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@\"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=opa334@protonmail.com&item_name=TrollStore\"] options:@{} completionHandler:^(BOOL success){}];\n}\n\n- (NSMutableArray*)specifiers\n{\n\tif(!_specifiers)\n\t{\n\t\t_specifiers = [NSMutableArray new];\n\t\t\n\t\tPSSpecifier* alfieGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\talfieGroupSpecifier.name = @\"Alfie\";\n\t\t[alfieGroupSpecifier setProperty:@\"Alfie found the new CoreTrust bug (CVE-2023-41991) via patchdiffing, produced a POC binary and worked on automatically applying it with the help of the ChOma library, while also contributing to said library.\" forKey:@\"footerText\"];\n\t\t[_specifiers addObject:alfieGroupSpecifier];\n\n\t\tPSSpecifier* alfieDonateSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Donate to alfiecg_dev\"\n\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\tedit:nil];\n\t\talfieDonateSpecifier.identifier = @\"donateToAlfie\";\n\t\t[alfieDonateSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\talfieDonateSpecifier.buttonAction = @selector(donateToAlfiePressed);\n\t\t[_specifiers addObject:alfieDonateSpecifier];\n\n\t\tPSSpecifier* opaGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\topaGroupSpecifier.name = @\"Opa\";\n\t\t[opaGroupSpecifier setProperty:@\"Opa developed the ChOma library, helped with automating the bug using it and integrated it into TrollStore.\" forKey:@\"footerText\"];\n\t\t[_specifiers addObject:opaGroupSpecifier];\n\n\t\tPSSpecifier* opaDonateSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Donate to opa334\"\n\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\tedit:nil];\n\t\topaDonateSpecifier.identifier = @\"donateToOpa\";\n\t\t[opaDonateSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\topaDonateSpecifier.buttonAction = @selector(donateToOpaPressed);\n\t\t[_specifiers addObject:opaDonateSpecifier];\n\t}\n\t[(UINavigationItem *)self.navigationItem setTitle:@\"Donate\"];\n\treturn _specifiers;\n}\n\n@end"
  },
  {
    "path": "TrollStore/TSInstallationController.h",
    "content": "@import Foundation;\n\n@interface TSInstallationController : NSObject\n\n+ (void)presentInstallationAlertIfEnabledForFile:(NSString*)pathToIPA isRemoteInstall:(BOOL)remoteInstall completion:(void (^)(BOOL, NSError*))completionBlock;\n\n+ (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completion;\n+ (void)handleAppInstallFromFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completion;\n\n+ (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completion;\n\n+ (void)installLdid;\n\n@end"
  },
  {
    "path": "TrollStore/TSInstallationController.m",
    "content": "#import \"TSInstallationController.h\"\n\n#import \"TSApplicationsManager.h\"\n#import \"TSAppInfo.h\"\n#import <TSUtil.h>\n#import <TSPresentationDelegate.h>\n\nextern NSUserDefaults* trollStoreUserDefaults(void);\n\n@implementation TSInstallationController\n\n+ (void)handleAppInstallFromFile:(NSString*)pathToIPA forceInstall:(BOOL)force completion:(void (^)(BOOL, NSError*))completionBlock\n{\n\tdispatch_async(dispatch_get_main_queue(), ^\n\t{\n\t\t[TSPresentationDelegate startActivity:@\"Installing\"];\n\t\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^\n\t\t{\n\t\t\t// Install IPA\n\t\t\tNSString* log;\n\t\t\tint ret = [[TSApplicationsManager sharedInstance] installIpa:pathToIPA force:force log:&log];\n\n\t\t\tNSError* error;\n\t\t\tif(ret != 0)\n\t\t\t{\n\t\t\t\terror = [[TSApplicationsManager sharedInstance] errorForCode:ret];\n\t\t\t}\n\n\t\t\tNSLog(@\"installed app! ret:%d, error: %@\", ret, error);\n\n\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t{\n\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:^\n\t\t\t\t{\n\t\t\t\t\tif (ret == 0) {\n\t\t\t\t\t\t// success\n\t\t\t\t\t\tif(completionBlock) completionBlock(YES, nil);\n\t\t\t\t\t} else if (ret == 171) {\n\t\t\t\t\t\t// recoverable error\n\t\t\t\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@\"Install Error %d\", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(completionBlock) completionBlock(NO, error);\n\t\t\t\t\t\t}];\n\t\t\t\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\t\t\t\tUIAlertAction* forceInstallAction = [UIAlertAction actionWithTitle:@\"Force Installation\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[self handleAppInstallFromFile:pathToIPA forceInstall:YES completion:completionBlock];\n\t\t\t\t\t\t}];\n\t\t\t\t\t\t[errorAlert addAction:forceInstallAction];\n\n\t\t\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t\t\t\t} else if (ret == 182) {\n\t\t\t\t\t\t// non-fatal informative message\n\t\t\t\t\t\tUIAlertController* rebootNotification = [UIAlertController alertControllerWithTitle:@\"Reboot Required\" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(completionBlock) completionBlock(YES, nil);\n\t\t\t\t\t\t}];\n\t\t\t\t\t\t[rebootNotification addAction:closeAction];\n\n\t\t\t\t\t\tUIAlertAction* rebootAction = [UIAlertAction actionWithTitle:@\"Reboot Now\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(completionBlock) completionBlock(YES, nil);\n\t\t\t\t\t\t\tspawnRoot(rootHelperPath(), @[@\"reboot\"], nil, nil);\n\t\t\t\t\t\t}];\n\t\t\t\t\t\t[rebootNotification addAction:rebootAction];\n\n\t\t\t\t\t\t[TSPresentationDelegate presentViewController:rebootNotification animated:YES completion:nil];\n\t\t\t\t\t} else if (ret == 184) {\n\t\t\t\t\t\t// warning\n\t\t\t\t\t\tUIAlertController* warningAlert = [UIAlertController alertControllerWithTitle:@\"Warning\" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(completionBlock) completionBlock(YES, nil);\n\t\t\t\t\t\t}];\n\t\t\t\t\t\t[warningAlert addAction:closeAction];\n\n\t\t\t\t\t\t[TSPresentationDelegate presentViewController:warningAlert animated:YES completion:nil];\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// unrecoverable error\n\t\t\t\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@\"Install Error %d\", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\t\t\t\tUIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@\"Copy Debug Log\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tUIPasteboard* pasteboard = [UIPasteboard generalPasteboard];\n\t\t\t\t\t\t\tpasteboard.string = log;\n\t\t\t\t\t\t}];\n\t\t\t\t\t\t[errorAlert addAction:copyLogAction];\n\n\t\t\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\n\t\t\t\t\t\tif(completionBlock) completionBlock(NO, error);\n\t\t\t\t\t}\n\t\t\t\t}];\n\t\t\t});\n\t\t});\n\t});\n}\n\n+ (void)presentInstallationAlertIfEnabledForFile:(NSString*)pathToIPA isRemoteInstall:(BOOL)remoteInstall completion:(void (^)(BOOL, NSError*))completionBlock\n{\n\tNSNumber* installAlertConfigurationNum = [trollStoreUserDefaults() objectForKey:@\"installAlertConfiguration\"];\n\tNSUInteger installAlertConfiguration = 0;\n\tif(installAlertConfigurationNum)\n\t{\n\t\tinstallAlertConfiguration = installAlertConfigurationNum.unsignedIntegerValue;\n\t\tif(installAlertConfiguration > 2)\n\t\t{\n\t\t\t// broken pref? revert to 0\n\t\t\tinstallAlertConfiguration = 0;\n\t\t}\n\t}\n\n\t// Check if user disabled alert for this kind of install\n\tif(installAlertConfiguration > 0)\n\t{\n\t\tif(installAlertConfiguration == 2 || (installAlertConfiguration == 1 && !remoteInstall))\n\t\t{\n\t\t\t[self handleAppInstallFromFile:pathToIPA completion:completionBlock];\n\t\t\treturn;\n\t\t}\n\t}\n\n\tTSAppInfo* appInfo = [[TSAppInfo alloc] initWithIPAPath:pathToIPA];\n\t[appInfo loadInfoWithCompletion:^(NSError* error)\n\t{\n\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t{\n\t\t\tif(!error)\n\t\t\t{\n\t\t\t\tUIAlertController* installAlert = [UIAlertController alertControllerWithTitle:@\"\" message:@\"\" preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\tinstallAlert.attributedTitle = [appInfo detailedInfoTitle];\n\t\t\t\tinstallAlert.attributedMessage = [appInfo detailedInfoDescription];\n\t\t\t\tUIAlertAction* installAction = [UIAlertAction actionWithTitle:@\"Install\" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action)\n\t\t\t\t{\n\t\t\t\t\t[self handleAppInstallFromFile:pathToIPA completion:completionBlock];\n\t\t\t\t}];\n\t\t\t\t[installAlert addAction:installAction];\n\n\t\t\t\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)\n\t\t\t\t{\n\t\t\t\t\tif(completionBlock) completionBlock(NO, nil);\n\t\t\t\t}];\n\t\t\t\t[installAlert addAction:cancelAction];\n\n\t\t\t\t[TSPresentationDelegate presentViewController:installAlert animated:YES completion:nil];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@\"Parse Error %ld\", error.code] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t\t}\n\t\t});\n\t}];\n}\n\n+ (void)handleAppInstallFromFile:(NSString*)pathToIPA completion:(void (^)(BOOL, NSError*))completionBlock\n{\n\t[self handleAppInstallFromFile:pathToIPA forceInstall:NO completion:completionBlock];\n}\n\n+ (void)handleAppInstallFromRemoteURL:(NSURL*)remoteURL completion:(void (^)(BOOL, NSError*))completionBlock\n{\n\tNSURLRequest* downloadRequest = [NSURLRequest requestWithURL:remoteURL];\n\n\tdispatch_async(dispatch_get_main_queue(), ^\n\t{\n\t\tNSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:downloadRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)\n\t\t{\n\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t{\n\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:^\n\t\t\t\t{\n\t\t\t\t\tif(error)\n\t\t\t\t\t{\n\t\t\t\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@\"Error\" message:[NSString stringWithFormat:@\"Error downloading app: %@\", error] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:^\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(completionBlock) completionBlock(NO, error);\n\t\t\t\t\t\t}];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tNSString* tmpIpaPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@\"tmp.ipa\"];\n\t\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil];\n\t\t\t\t\t\t[[NSFileManager defaultManager] moveItemAtPath:location.path toPath:tmpIpaPath error:nil];\n\t\t\t\t\t\t[self presentInstallationAlertIfEnabledForFile:tmpIpaPath isRemoteInstall:YES completion:^(BOOL success, NSError* error)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtPath:tmpIpaPath error:nil];\n\t\t\t\t\t\t\tif(completionBlock) completionBlock(success, error);\n\t\t\t\t\t\t}];\n\t\t\t\t\t}\n\t\t\t\t}];\n\t\t\t});\n\t\t}];\n\n\t\t[TSPresentationDelegate startActivity:@\"Downloading\" withCancelHandler:^\n\t\t{\n\t\t\t[downloadTask cancel];\n\t\t}];\n\n\t\t[downloadTask resume];\n\t});\n}\n\n+ (void)installLdid\n{\n\tfetchLatestLdidVersion(^(NSString* latestVersion)\n\t{\n\t\tif(!latestVersion) return;\n\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t{\n\t\t\tNSURL* ldidURL = [NSURL URLWithString:@\"https://github.com/opa334/ldid/releases/latest/download/ldid\"];\n\t\t\tNSURLRequest* ldidRequest = [NSURLRequest requestWithURL:ldidURL];\n\n\t\t\t[TSPresentationDelegate startActivity:@\"Installing ldid\"];\n\n\t\t\tNSURLSessionDownloadTask* downloadTask = [NSURLSession.sharedSession downloadTaskWithRequest:ldidRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)\n\t\t\t{\n\t\t\t\tif(error)\n\t\t\t\t{\n\t\t\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@\"Error\" message:[NSString stringWithFormat:@\"Error downloading ldid: %@\", error] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t\t\t{\n\t\t\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:^\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t\t\t\t\t}];\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse if(location)\n\t\t\t\t{\n\t\t\t\t\tspawnRoot(rootHelperPath(), @[@\"install-ldid\", location.path, latestVersion], nil, nil);\n\t\t\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t\t\t{\n\t\t\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:nil];\n\t\t\t\t\t\t[[NSNotificationCenter defaultCenter] postNotificationName:@\"TrollStoreReloadSettingsNotification\" object:nil userInfo:nil];\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}];\n\n\t\t\t[downloadTask resume];\n\t\t});\n\t});\n}\n\n@end"
  },
  {
    "path": "TrollStore/TSRootViewController.h",
    "content": "#import <UIKit/UIKit.h>\n\n@interface TSRootViewController : UITabBarController\n\n@end\n"
  },
  {
    "path": "TrollStore/TSRootViewController.m",
    "content": "#import \"TSRootViewController.h\"\n#import \"TSAppTableViewController.h\"\n#import \"TSSettingsListController.h\"\n#import <TSPresentationDelegate.h>\n\n@implementation TSRootViewController\n\n- (void)loadView {\n\t[super loadView];\n\n\tTSAppTableViewController* appTableVC = [[TSAppTableViewController alloc] init];\n\tappTableVC.title = @\"Apps\";\n\n\tTSSettingsListController* settingsListVC = [[TSSettingsListController alloc] init];\n\tsettingsListVC.title = @\"Settings\";\n\n\tUINavigationController* appNavigationController = [[UINavigationController alloc] initWithRootViewController:appTableVC];\n\tUINavigationController* settingsNavigationController = [[UINavigationController alloc] initWithRootViewController:settingsListVC];\n\t\n\tappNavigationController.tabBarItem.image = [UIImage systemImageNamed:@\"square.stack.3d.up.fill\"];\n\tsettingsNavigationController.tabBarItem.image = [UIImage systemImageNamed:@\"gear\"];\n\n\tself.title = @\"Root View Controller\";\n\tself.viewControllers = @[appNavigationController, settingsNavigationController];\n}\n\n- (void)viewDidLoad\n{\n\t[super viewDidLoad];\n\n\tTSPresentationDelegate.presentationViewController = self;\n}\n\n@end\n"
  },
  {
    "path": "TrollStore/TSSceneDelegate.h",
    "content": "#import <UIKit/UIKit.h>\n\n@interface TSSceneDelegate : UIResponder <UIWindowSceneDelegate>\n@property (strong, nonatomic) UIWindow * window;\n@property (nonatomic, strong) UITabBarController *rootViewController;\n@end\n"
  },
  {
    "path": "TrollStore/TSSceneDelegate.m",
    "content": "#import \"TSSceneDelegate.h\"\n#import \"TSRootViewController.h\"\n#import \"TSUtil.h\"\n#import \"TSApplicationsManager.h\"\n#import \"TSInstallationController.h\"\n#import <TSPresentationDelegate.h>\n\n@implementation TSSceneDelegate\n\n- (void)handleURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts scene:(UIWindowScene*)scene\n{\n\tfor(UIOpenURLContext* context in URLContexts)\n\t{\n\t\tNSURL* url = context.URL;\n\n\t\tif(url)\n\t\t{\n\t\t\tif([url isFileURL])\n\t\t\t{\n\t\t\t\t[url startAccessingSecurityScopedResource];\n\t\t\t\tvoid (^doneBlock)(BOOL) = ^(BOOL shouldExit)\n\t\t\t\t{\n\t\t\t\t\t[url stopAccessingSecurityScopedResource];\n\t\t\t\t\t[[NSFileManager defaultManager] removeItemAtURL:url error:nil];\n\n\t\t\t\t\tif(shouldExit)\n\t\t\t\t\t{\n\t\t\t\t\t\tNSLog(@\"Respring + Exit\");\n\t\t\t\t\t\trespring();\n\t\t\t\t\t\texit(0);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\tif ([url.pathExtension.lowercaseString isEqualToString:@\"ipa\"] || [url.pathExtension.lowercaseString isEqualToString:@\"tipa\"])\n\t\t\t\t{\n\t\t\t\t\t[TSInstallationController presentInstallationAlertIfEnabledForFile:url.path isRemoteInstall:NO completion:^(BOOL success, NSError* error){\n\t\t\t\t\t\tdoneBlock(NO);\n\t\t\t\t\t}];\n\t\t\t\t}\n\t\t\t\telse if([url.pathExtension.lowercaseString isEqualToString:@\"tar\"])\n\t\t\t\t{\n\t\t\t\t\t// Update TrollStore itself\n\t\t\t\t\tNSLog(@\"Updating TrollStore...\");\n\t\t\t\t\tint ret = spawnRoot(rootHelperPath(), @[@\"install-trollstore\", url.path], nil, nil);\n\t\t\t\t\tdoneBlock(ret == 0);\n\t\t\t\t\tNSLog(@\"Updated TrollStore!\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if([url.scheme isEqualToString:@\"apple-magnifier\"])\n\t\t\t{\n\t\t\t\tNSURLComponents* components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];\n\t\t\t\tif([components.host isEqualToString:@\"install\"])\n\t\t\t\t{\n\t\t\t\t\tNSString* URLStringToInstall;\n\n\t\t\t\t\tfor(NSURLQueryItem* queryItem in components.queryItems)\n\t\t\t\t\t{\n\t\t\t\t\t\tif([queryItem.name isEqualToString:@\"url\"])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tURLStringToInstall = queryItem.value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif(URLStringToInstall && [URLStringToInstall isKindOfClass:NSString.class])\n\t\t\t\t\t{\n\t\t\t\t\t\tNSURL* URLToInstall = [NSURL URLWithString:URLStringToInstall];\n\t\t\t\t\t\t[TSInstallationController handleAppInstallFromRemoteURL:URLToInstall completion:nil];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if([components.host isEqualToString:@\"enable-jit\"])\n\t\t\t\t{\n\t\t\t\t\tNSString* BundleIDToEnableJIT;\n\n\t\t\t\t\tfor(NSURLQueryItem* queryItem in components.queryItems)\n\t\t\t\t\t{\n\t\t\t\t\t\tif([queryItem.name isEqualToString:@\"bundle-id\"])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBundleIDToEnableJIT = queryItem.value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif(BundleIDToEnableJIT && [BundleIDToEnableJIT isKindOfClass:NSString.class])\n\t\t\t\t\t{\n\t\t\t\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t{\n\t\t\t\t\t\t\t[self handleEnableJITForBundleID:BundleIDToEnableJIT];\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n- (void)handleEnableJITForBundleID:(NSString *)appId\n{\n\tTSApplicationsManager* appsManager = [TSApplicationsManager sharedInstance];\n\n\tBOOL didOpen = [appsManager openApplicationWithBundleID:appId];\n\n\t// if we failed to open the app, show an alert\n\tif(!didOpen)\n\t{\n\t\tNSString* failMessage = @\"\";\n\t\t// we don't have TSAppInfo here so we cannot check the registration state\n\n\t\tNSString* failTitle = [NSString stringWithFormat:@\"Failed to open %@\", appId];\n\t\tUIAlertController* didFailController = [UIAlertController alertControllerWithTitle:failTitle message:failMessage preferredStyle:UIAlertControllerStyleAlert];\n\t\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\n\t\t[didFailController addAction:cancelAction];\n\t\t[TSPresentationDelegate presentViewController:didFailController animated:YES completion:nil];\n\t}\n\telse\n\t{\n\t\tint ret = [appsManager enableJITForBundleID:appId];\n\t\tif (ret != 0)\n\t\t{\n\t\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:@\"Error\" message:[NSString stringWithFormat:@\"Error enabling JIT: trollstorehelper returned %d\", ret] preferredStyle:UIAlertControllerStyleAlert];\n\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t[errorAlert addAction:closeAction];\n\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t}\n\t}\n}\n\n// We want to auto install ldid if either it doesn't exist\n// or if it's the one from an old TrollStore version that's no longer supported\n- (void)handleLdidCheck\n{\n#ifndef TROLLSTORE_LITE\n\t//if (@available(iOS 16, *)) {} else {\n\t\tNSString* tsAppPath = [NSBundle mainBundle].bundlePath;\n\n\t\tNSString* ldidPath = [tsAppPath stringByAppendingPathComponent:@\"ldid\"];\n\t\tNSString* ldidVersionPath = [tsAppPath stringByAppendingPathComponent:@\"ldid.version\"];\n\n\t\tif(![[NSFileManager defaultManager] fileExistsAtPath:ldidPath] || ![[NSFileManager defaultManager] fileExistsAtPath:ldidVersionPath])\n\t\t{\n\t\t\t[TSInstallationController installLdid];\n\t\t}\n\t//}\n#endif\n}\n\n- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {\n\t// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.\n\t// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.\n\t// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).\n\t\n\tUIWindowScene* windowScene = (UIWindowScene*)scene;\n\t_window = [[UIWindow alloc] initWithWindowScene:windowScene];\n\t_rootViewController = [[TSRootViewController alloc] init];\n\t_window.rootViewController = _rootViewController;\n\t[_window makeKeyAndVisible];\n\n\tif(connectionOptions.URLContexts.count)\n\t{\n\t\t[self handleURLContexts:connectionOptions.URLContexts scene:(UIWindowScene*)scene];\n\t}\n\telse\n\t{\n\t\t[self handleLdidCheck];\n\t}\n}\n\n\n- (void)sceneDidDisconnect:(UIScene *)scene {\n\t// Called as the scene is being released by the system.\n\t// This occurs shortly after the scene enters the background, or when its session is discarded.\n\t// Release any resources associated with this scene that can be re-created the next time the scene connects.\n\t// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).\n}\n\n\n- (void)sceneDidBecomeActive:(UIScene *)scene {\n\t// Called when the scene has moved from an inactive state to an active state.\n\t// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.\n}\n\n\n- (void)sceneWillResignActive:(UIScene *)scene {\n\t// Called when the scene will move from an active state to an inactive state.\n\t// This may occur due to temporary interruptions (ex. an incoming phone call).\n}\n\n\n- (void)sceneWillEnterForeground:(UIScene *)scene {\n\t// Called as the scene transitions from the background to the foreground.\n\t// Use this method to undo the changes made on entering the background.\n}\n\n\n- (void)sceneDidEnterBackground:(UIScene *)scene {\n\t// Called as the scene transitions from the foreground to the background.\n\t// Use this method to save data, release shared resources, and store enough scene-specific state information\n\t// to restore the scene back to its current state.\n}\n\n- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts\n{\n\t[self handleURLContexts:URLContexts scene:(UIWindowScene*)scene];\n}\n\n@end"
  },
  {
    "path": "TrollStore/TSSettingsAdvancedListController.h",
    "content": "#import <Preferences/PSListController.h>\n\n@interface TSSettingsAdvancedListController : PSListController\n\n@end"
  },
  {
    "path": "TrollStore/TSSettingsAdvancedListController.m",
    "content": "#import \"TSSettingsAdvancedListController.h\"\n#import \"TSUtil.h\"\n#import <Preferences/PSSpecifier.h>\n\nextern NSUserDefaults* trollStoreUserDefaults();\n@interface PSSpecifier ()\n@property (nonatomic,retain) NSArray* values;\n@end\n\n@implementation TSSettingsAdvancedListController\n\n- (NSMutableArray*)specifiers\n{\n\tif(!_specifiers)\n\t{\n\t\t_specifiers = [NSMutableArray new];\n\n\t\tPSSpecifier* installationMethodGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t//installationMethodGroupSpecifier.name = @\"Installation\";\n\t\t[installationMethodGroupSpecifier setProperty:@\"installd:\\nInstalls applications by doing a placeholder installation through installd, fixing the permissions and then adding it to icon cache.\\nAdvantage: Might be slightly more persistent than the custom method in terms of icon cache reloads.\\nDisadvantage: Causes some small issues with certain applications for seemingly no reason (E.g. Watusi cannot save preferences when being installed using this method).\\n\\nCustom (Recommended):\\nInstalls applications by manually creating a bundle using MobileContainerManager, copying the app into it and adding it to icon cache.\\nAdvantage: No known issues (As opposed to the Watusi issue outlined in the installd method).\\nDisadvantage: Might be slightly less persistent then the installd method in terms of icon cache reloads.\\n\\nNOTE: In cases where installd is selected but the placeholder installation fails, TrollStore automatically falls back to using the Custom method.\" forKey:@\"footerText\"];\n\t\t[_specifiers addObject:installationMethodGroupSpecifier];\n\n\t\tPSSpecifier* installationMethodSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Installation Method\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSStaticTextCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t[installationMethodSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\tinstallationMethodSpecifier.identifier = @\"installationMethodLabel\";\n\t\t[_specifiers addObject:installationMethodSpecifier];\n\n\t\tPSSpecifier* installationMethodSegmentSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Installation Method Segment\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:@selector(setPreferenceValue:specifier:)\n\t\t\t\t\t\t\t\t\t\t\tget:@selector(readPreferenceValue:)\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSSegmentCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t[installationMethodSegmentSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\tinstallationMethodSegmentSpecifier.identifier = @\"installationMethodSegment\";\n\t\t[installationMethodSegmentSpecifier setProperty:APP_ID forKey:@\"defaults\"];\n\t\t[installationMethodSegmentSpecifier setProperty:@\"installationMethod\" forKey:@\"key\"];\n\t\tinstallationMethodSegmentSpecifier.values = @[@0, @1];\n\t\tinstallationMethodSegmentSpecifier.titleDictionary = @{@0 : @\"installd\", @1 : @\"Custom\"};\n\t\t[installationMethodSegmentSpecifier setProperty:@1 forKey:@\"default\"];\n\t\t[_specifiers addObject:installationMethodSegmentSpecifier];\n\n\t\tPSSpecifier* uninstallationMethodGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t//uninstallationMethodGroupSpecifier.name = @\"Uninstallation\";\n\t\t[uninstallationMethodGroupSpecifier setProperty:@\"installd (Recommended):\\nUninstalls applications using the same API that SpringBoard uses when uninstalling them from the home screen.\\n\\nCustom:\\nUninstalls applications by removing them from icon cache and then deleting their application and data bundles directly.\\n\\nNOTE: In cases where installd is selected but the stock uninstallation fails, TrollStore automatically falls back to using the Custom method.\" forKey:@\"footerText\"];\n\t\t[_specifiers addObject:uninstallationMethodGroupSpecifier];\n\n\t\tPSSpecifier* uninstallationMethodSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Uninstallation Method\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSStaticTextCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t[uninstallationMethodSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\tuninstallationMethodSpecifier.identifier = @\"uninstallationMethodLabel\";\n\t\t[_specifiers addObject:uninstallationMethodSpecifier];\n\n\t\tPSSpecifier* uninstallationMethodSegmentSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Installation Method Segment\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:@selector(setPreferenceValue:specifier:)\n\t\t\t\t\t\t\t\t\t\t\tget:@selector(readPreferenceValue:)\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSSegmentCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t[uninstallationMethodSegmentSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\tuninstallationMethodSegmentSpecifier.identifier = @\"uninstallationMethodSegment\";\n\t\t[uninstallationMethodSegmentSpecifier setProperty:APP_ID forKey:@\"defaults\"];\n\t\t[uninstallationMethodSegmentSpecifier setProperty:@\"uninstallationMethod\" forKey:@\"key\"];\n\t\tuninstallationMethodSegmentSpecifier.values = @[@0, @1];\n\t\tuninstallationMethodSegmentSpecifier.titleDictionary = @{@0 : @\"installd\", @1 : @\"Custom\"};\n\t\t[uninstallationMethodSegmentSpecifier setProperty:@0 forKey:@\"default\"];\n\t\t[_specifiers addObject:uninstallationMethodSegmentSpecifier];\n\t}\n\n\t[(UINavigationItem *)self.navigationItem setTitle:@\"Advanced\"];\n\treturn _specifiers;\n}\n\n- (void)setPreferenceValue:(NSObject*)value specifier:(PSSpecifier*)specifier\n{\n\tNSUserDefaults* tsDefaults = trollStoreUserDefaults();\n\t[tsDefaults setObject:value forKey:[specifier propertyForKey:@\"key\"]];\n}\n\n- (NSObject*)readPreferenceValue:(PSSpecifier*)specifier\n{\n\tNSUserDefaults* tsDefaults = trollStoreUserDefaults();\n\tNSObject* toReturn = [tsDefaults objectForKey:[specifier propertyForKey:@\"key\"]];\n\tif(!toReturn)\n\t{\n\t\ttoReturn = [specifier propertyForKey:@\"default\"];\n\t}\n\treturn toReturn;\n}\n\n@end\n"
  },
  {
    "path": "TrollStore/TSSettingsListController.h",
    "content": "#import \"TSListControllerShared.h\"\n\n@interface TSSettingsListController : TSListControllerShared\n{\n    PSSpecifier* _installPersistenceHelperSpecifier;\n    NSString* _newerVersion;\n    NSString* _newerLdidVersion;\n    BOOL _devModeEnabled;\n}\n@end"
  },
  {
    "path": "TrollStore/TSSettingsListController.m",
    "content": "#import \"TSSettingsListController.h\"\n#import <TSUtil.h>\n#import <Preferences/PSSpecifier.h>\n#import <Preferences/PSListItemsController.h>\n#import <TSPresentationDelegate.h>\n#import \"TSInstallationController.h\"\n#import \"TSSettingsAdvancedListController.h\"\n#import \"TSDonateListController.h\"\n\n@interface NSUserDefaults (Private)\n- (instancetype)_initWithSuiteName:(NSString *)suiteName container:(NSURL *)container;\n@end\nextern NSUserDefaults* trollStoreUserDefaults(void);\n\n@implementation TSSettingsListController\n\n- (void)viewDidLoad\n{\n\t[super viewDidLoad];\n\t[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:UIApplicationWillEnterForegroundNotification object:nil];\n\t[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadSpecifiers) name:@\"TrollStoreReloadSettingsNotification\" object:nil];\n\n#ifndef TROLLSTORE_LITE\n\tfetchLatestTrollStoreVersion(^(NSString* latestVersion)\n\t{\n\t\tNSString* currentVersion = [self getTrollStoreVersion];\n\t\tNSComparisonResult result = [currentVersion compare:latestVersion options:NSNumericSearch];\n\t\tif(result == NSOrderedAscending)\n\t\t{\n\t\t\t_newerVersion = latestVersion;\n\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t{\n\t\t\t\t[self reloadSpecifiers];\n\t\t\t});\n\t\t}\n\t});\n\n\t//if (@available(iOS 16, *)) {} else {\n\t\tfetchLatestLdidVersion(^(NSString* latestVersion)\n\t\t{\n\t\t\tNSString* ldidVersionPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@\"ldid.version\"];\n\t\t\tNSString* ldidVersion = nil;\n\t\t\tNSData* ldidVersionData = [NSData dataWithContentsOfFile:ldidVersionPath];\n\t\t\tif(ldidVersionData)\n\t\t\t{\n\t\t\t\tldidVersion = [[NSString alloc] initWithData:ldidVersionData encoding:NSUTF8StringEncoding];\n\t\t\t}\n\t\t\t\n\t\t\tif(![latestVersion isEqualToString:ldidVersion])\n\t\t\t{\n\t\t\t\t_newerLdidVersion = latestVersion;\n\t\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t\t{\n\t\t\t\t\t[self reloadSpecifiers];\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t//}\n\n\tif (@available(iOS 16, *))\n\t{\n\t\t_devModeEnabled = spawnRoot(rootHelperPath(), @[@\"check-dev-mode\"], nil, nil) == 0;\n\t}\n\telse\n\t{\n\t\t_devModeEnabled = YES;\n\t}\n#endif\n\t[self reloadSpecifiers];\n}\n\n- (NSMutableArray*)specifiers\n{\n\tif(!_specifiers)\n\t{\n\t\t_specifiers = [NSMutableArray new];\n\n#ifndef TROLLSTORE_LITE\n\t\tif(_newerVersion)\n\t\t{\n\t\t\tPSSpecifier* updateTrollStoreGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t\tupdateTrollStoreGroupSpecifier.name = @\"Update Available\";\n\t\t\t[_specifiers addObject:updateTrollStoreGroupSpecifier];\n\n\t\t\tPSSpecifier* updateTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:[NSString stringWithFormat:@\"Update TrollStore to %@\", _newerVersion]\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\tupdateTrollStoreSpecifier.identifier = @\"updateTrollStore\";\n\t\t\t[updateTrollStoreSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\tupdateTrollStoreSpecifier.buttonAction = @selector(updateTrollStorePressed);\n\t\t\t[_specifiers addObject:updateTrollStoreSpecifier];\n\t\t}\n\n\t\tif(!_devModeEnabled)\n\t\t{\n\t\t\tPSSpecifier* enableDevModeGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t\tenableDevModeGroupSpecifier.name = @\"Developer Mode\";\n\t\t\t[enableDevModeGroupSpecifier setProperty:@\"Some apps require developer mode enabled to launch. This requires a reboot to take effect.\" forKey:@\"footerText\"];\n\t\t\t[_specifiers addObject:enableDevModeGroupSpecifier];\n\n\t\t\tPSSpecifier* enableDevModeSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Enable Developer Mode\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\tenableDevModeSpecifier.identifier = @\"enableDevMode\";\n\t\t\t[enableDevModeSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\tenableDevModeSpecifier.buttonAction = @selector(enableDevModePressed);\n\t\t\t[_specifiers addObject:enableDevModeSpecifier];\n\t\t}\n#endif\n\n\t\tPSSpecifier* utilitiesGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\tutilitiesGroupSpecifier.name = @\"Utilities\";\n\n\t\tNSString *utilitiesDescription = @\"\";\n#ifdef TROLLSTORE_LITE\n\t\tif (shouldRegisterAsUserByDefault()) {\n\t\t\tutilitiesDescription = @\"Apps will be registered as User by default since AppSync Unified is installed.\\n\\n\";\n\t\t}\n\t\telse {\n\t\t\tutilitiesDescription = @\"Apps will be registered as System by default since AppSync Unified is not installed. When apps loose their System registration and stop working, press \\\"Refresh App Registrations\\\" here to fix them.\\n\\n\";\n\t\t}\n#endif\n\t\tutilitiesDescription = [utilitiesDescription stringByAppendingString:@\"If an app does not immediately appear after installation, respring here and it should appear afterwards.\"];\n\n\t\t[utilitiesGroupSpecifier setProperty:utilitiesDescription forKey:@\"footerText\"];\n\t\t[_specifiers addObject:utilitiesGroupSpecifier];\n\n\t\tPSSpecifier* respringButtonSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Respring\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t respringButtonSpecifier.identifier = @\"respring\";\n\t\t[respringButtonSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\trespringButtonSpecifier.buttonAction = @selector(respringButtonPressed);\n\n\t\t[_specifiers addObject:respringButtonSpecifier];\n\n\t\tPSSpecifier* refreshAppRegistrationsSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Refresh App Registrations\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\trefreshAppRegistrationsSpecifier.identifier = @\"refreshAppRegistrations\";\n\t\t[refreshAppRegistrationsSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\trefreshAppRegistrationsSpecifier.buttonAction = @selector(refreshAppRegistrationsPressed);\n\n\t\t[_specifiers addObject:refreshAppRegistrationsSpecifier];\n\n\t\tPSSpecifier* rebuildIconCacheSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Rebuild Icon Cache\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t rebuildIconCacheSpecifier.identifier = @\"uicache\";\n\t\t[rebuildIconCacheSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\trebuildIconCacheSpecifier.buttonAction = @selector(rebuildIconCachePressed);\n\n\t\t[_specifiers addObject:rebuildIconCacheSpecifier];\n\n\t\tNSArray *inactiveBundlePaths = trollStoreInactiveInstalledAppBundlePaths();\n\t\tif (inactiveBundlePaths.count > 0) {\n\t\t\tPSSpecifier* transferAppsSpecifier = [PSSpecifier preferenceSpecifierNamed:[NSString stringWithFormat:@\"Transfer %zu \"OTHER_APP_NAME@\" %@\", inactiveBundlePaths.count, inactiveBundlePaths.count > 1 ? @\"Apps\" : @\"App\"]\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\ttransferAppsSpecifier.identifier = @\"transferApps\";\n\t\t\t[transferAppsSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\ttransferAppsSpecifier.buttonAction = @selector(transferAppsPressed);\n\n\t\t\t[_specifiers addObject:transferAppsSpecifier];\n\t\t}\n\n#ifndef TROLLSTORE_LITE\n\t\t//if (@available(iOS 16, *)) { } else {\n\t\t\tNSString* ldidPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@\"ldid\"];\n\t\t\tNSString* ldidVersionPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@\"ldid.version\"];\n\t\t\tBOOL ldidInstalled = [[NSFileManager defaultManager] fileExistsAtPath:ldidPath];\n\n\t\t\tNSString* ldidVersion = nil;\n\t\t\tNSData* ldidVersionData = [NSData dataWithContentsOfFile:ldidVersionPath];\n\t\t\tif(ldidVersionData)\n\t\t\t{\n\t\t\t\tldidVersion = [[NSString alloc] initWithData:ldidVersionData encoding:NSUTF8StringEncoding];\n\t\t\t}\n\n\t\t\tPSSpecifier* signingGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t\tsigningGroupSpecifier.name = @\"Signing\";\n\n\t\t\tif(ldidInstalled)\n\t\t\t{\n\t\t\t\t[signingGroupSpecifier setProperty:@\"ldid is installed and allows TrollStore to install unsigned IPA files.\" forKey:@\"footerText\"];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t[signingGroupSpecifier setProperty:@\"In order for TrollStore to be able to install unsigned IPAs, ldid has to be installed using this button. It can't be directly included in TrollStore because of licensing issues.\" forKey:@\"footerText\"];\n\t\t\t}\n\n\t\t\t[_specifiers addObject:signingGroupSpecifier];\n\n\t\t\tif(ldidInstalled)\n\t\t\t{\n\t\t\t\tNSString* installedTitle = @\"ldid: Installed\";\n\t\t\t\tif(ldidVersion)\n\t\t\t\t{\n\t\t\t\t\tinstalledTitle = [NSString stringWithFormat:@\"%@ (%@)\", installedTitle, ldidVersion];\n\t\t\t\t}\n\n\t\t\t\tPSSpecifier* ldidInstalledSpecifier = [PSSpecifier preferenceSpecifierNamed:installedTitle\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSStaticTextCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t\t[ldidInstalledSpecifier setProperty:@NO forKey:@\"enabled\"];\n\t\t\t\tldidInstalledSpecifier.identifier = @\"ldidInstalled\";\n\t\t\t\t[_specifiers addObject:ldidInstalledSpecifier];\n\n\t\t\t\tif(_newerLdidVersion && ![_newerLdidVersion isEqualToString:ldidVersion])\n\t\t\t\t{\n\t\t\t\t\tNSString* updateTitle = [NSString stringWithFormat:@\"Update to %@\", _newerLdidVersion];\n\t\t\t\t\tPSSpecifier* ldidUpdateSpecifier = [PSSpecifier preferenceSpecifierNamed:updateTitle\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t\t\tldidUpdateSpecifier.identifier = @\"updateLdid\";\n\t\t\t\t\t[ldidUpdateSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t\t\tldidUpdateSpecifier.buttonAction = @selector(installOrUpdateLdidPressed);\n\t\t\t\t\t[_specifiers addObject:ldidUpdateSpecifier];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPSSpecifier* installLdidSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Install ldid\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t\tinstallLdidSpecifier.identifier = @\"installLdid\";\n\t\t\t\t[installLdidSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t\tinstallLdidSpecifier.buttonAction = @selector(installOrUpdateLdidPressed);\n\t\t\t\t[_specifiers addObject:installLdidSpecifier];\n\t\t\t}\n\t\t//}\n\n\t\tPSSpecifier* persistenceGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\tpersistenceGroupSpecifier.name = @\"Persistence\";\n\t\t[_specifiers addObject:persistenceGroupSpecifier];\n\n\t\tif([[NSFileManager defaultManager] fileExistsAtPath:@\"/Applications/TrollStorePersistenceHelper.app\"])\n\t\t{\n\t\t\t[persistenceGroupSpecifier setProperty:@\"When iOS rebuilds the icon cache, all TrollStore apps including TrollStore itself will be reverted to \\\"User\\\" state and either disappear or no longer launch. If that happens, you can use the TrollHelper app on the home screen to refresh the app registrations, which will make them work again.\" forKey:@\"footerText\"];\n\t\t\tPSSpecifier* installedPersistenceHelperSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Helper Installed as Standalone App\"\n\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\tcell:PSStaticTextCell\n\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t[installedPersistenceHelperSpecifier setProperty:@NO forKey:@\"enabled\"];\n\t\t\tinstalledPersistenceHelperSpecifier.identifier = @\"persistenceHelperInstalled\";\n\t\t\t[_specifiers addObject:installedPersistenceHelperSpecifier];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tLSApplicationProxy* persistenceApp = findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE_ALL);\n\t\t\tif(persistenceApp)\n\t\t\t{\n\t\t\t\tNSString* appName = [persistenceApp localizedName];\n\n\t\t\t\t[persistenceGroupSpecifier setProperty:[NSString stringWithFormat:@\"When iOS rebuilds the icon cache, all TrollStore apps including TrollStore itself will be reverted to \\\"User\\\" state and either disappear or no longer launch. If that happens, you can use the persistence helper installed into %@ to refresh the app registrations, which will make them work again.\", appName] forKey:@\"footerText\"];\n\t\t\t\tPSSpecifier* installedPersistenceHelperSpecifier = [PSSpecifier preferenceSpecifierNamed:[NSString stringWithFormat:@\"Helper Installed into %@\", appName]\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSStaticTextCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t\t[installedPersistenceHelperSpecifier setProperty:@NO forKey:@\"enabled\"];\n\t\t\t\tinstalledPersistenceHelperSpecifier.identifier = @\"persistenceHelperInstalled\";\n\t\t\t\t[_specifiers addObject:installedPersistenceHelperSpecifier];\n\n\t\t\t\tPSSpecifier* uninstallPersistenceHelperSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Uninstall Persistence Helper\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\n\t\t\t\tuninstallPersistenceHelperSpecifier.identifier = @\"uninstallPersistenceHelper\";\n\t\t\t\t[uninstallPersistenceHelperSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t\t[uninstallPersistenceHelperSpecifier setProperty:NSClassFromString(@\"PSDeleteButtonCell\") forKey:@\"cellClass\"];\n\t\t\t\tuninstallPersistenceHelperSpecifier.buttonAction = @selector(uninstallPersistenceHelperPressed);\n\t\t\t\t[_specifiers addObject:uninstallPersistenceHelperSpecifier];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t[persistenceGroupSpecifier setProperty:@\"When iOS rebuilds the icon cache, all TrollStore apps including TrollStore itself will be reverted to \\\"User\\\" state and either disappear or no longer launch. The only way to have persistence in a rootless environment is to replace a system application, here you can select a system app to replace with a persistence helper that can be used to refresh the registrations of all TrollStore related apps in case they disappear or no longer launch.\" forKey:@\"footerText\"];\n\n\t\t\t\t_installPersistenceHelperSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Install Persistence Helper\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\t\t\t_installPersistenceHelperSpecifier.identifier = @\"installPersistenceHelper\";\n\t\t\t\t[_installPersistenceHelperSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t\t\t_installPersistenceHelperSpecifier.buttonAction = @selector(installPersistenceHelperPressed);\n\t\t\t\t[_specifiers addObject:_installPersistenceHelperSpecifier];\n\t\t\t}\n\t\t}\n#endif\n\n\t\tPSSpecifier* installationSettingsGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\tinstallationSettingsGroupSpecifier.name = @\"Security\";\n\t\t[installationSettingsGroupSpecifier setProperty:@\"The URL Scheme, when enabled, will allow apps and websites to trigger TrollStore installations through the apple-magnifier://install?url=<IPA_URL> URL scheme and enable JIT through the apple-magnifier://enable-jit?bundle-id=<BUNDLE_ID> URL scheme.\" forKey:@\"footerText\"];\n\n\t\t[_specifiers addObject:installationSettingsGroupSpecifier];\n\n\t\tPSSpecifier* URLSchemeToggle = [PSSpecifier preferenceSpecifierNamed:@\"URL Scheme Enabled\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:@selector(setURLSchemeEnabled:forSpecifier:)\n\t\t\t\t\t\t\t\t\t\tget:@selector(getURLSchemeEnabledForSpecifier:)\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSSwitchCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\n\t\t[_specifiers addObject:URLSchemeToggle];\n\n\t\tPSSpecifier* installAlertConfigurationSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Show Install Confirmation Alert\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:@selector(setPreferenceValue:specifier:)\n\t\t\t\t\t\t\t\t\t\tget:@selector(readPreferenceValue:)\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSLinkListCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\n\t\tinstallAlertConfigurationSpecifier.detailControllerClass = [PSListItemsController class];\n\t\t[installAlertConfigurationSpecifier setProperty:@\"installationConfirmationValues\" forKey:@\"valuesDataSource\"];\n        [installAlertConfigurationSpecifier setProperty:@\"installationConfirmationNames\" forKey:@\"titlesDataSource\"];\n\t\t[installAlertConfigurationSpecifier setProperty:APP_ID forKey:@\"defaults\"];\n\t\t[installAlertConfigurationSpecifier setProperty:@\"installAlertConfiguration\" forKey:@\"key\"];\n        [installAlertConfigurationSpecifier setProperty:@0 forKey:@\"default\"];\n\n\t\t[_specifiers addObject:installAlertConfigurationSpecifier];\n\n\t\tPSSpecifier* otherGroupSpecifier = [PSSpecifier emptyGroupSpecifier];\n\t\t[otherGroupSpecifier setProperty:[NSString stringWithFormat:@\"%@ %@\\n\\n© 2022-2024 Lars Fröder (opa334)\\n\\nTrollStore is NOT for piracy!\\n\\nCredits:\\nGoogle TAG, @alfiecg_dev: CoreTrust bug\\n@lunotech11, @SerenaKit, @tylinux, @TheRealClarity, @dhinakg, @khanhduytran0: Various contributions\\n@ProcursusTeam: uicache, ldid\\n@cstar_ow: uicache\\n@saurik: ldid\", APP_NAME, [self getTrollStoreVersion]] forKey:@\"footerText\"];\n\t\t[_specifiers addObject:otherGroupSpecifier];\n\n\t\tPSSpecifier* advancedLinkSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Advanced\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSLinkListCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\tadvancedLinkSpecifier.detailControllerClass = [TSSettingsAdvancedListController class];\n\t\t[advancedLinkSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t[_specifiers addObject:advancedLinkSpecifier];\n\n\t\tPSSpecifier* donateSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Donate\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSLinkListCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\tdonateSpecifier.detailControllerClass = [TSDonateListController class];\n\t\t[donateSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t[_specifiers addObject:donateSpecifier];\n\n#ifndef TROLLSTORE_LITE\n\t\t// Uninstall TrollStore\n\t\tPSSpecifier* uninstallTrollStoreSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Uninstall TrollStore\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\tuninstallTrollStoreSpecifier.identifier = @\"uninstallTrollStore\";\n\t\t[uninstallTrollStoreSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\t[uninstallTrollStoreSpecifier setProperty:NSClassFromString(@\"PSDeleteButtonCell\") forKey:@\"cellClass\"];\n\t\tuninstallTrollStoreSpecifier.buttonAction = @selector(uninstallTrollStorePressed);\n\t\t[_specifiers addObject:uninstallTrollStoreSpecifier];\n#endif\n\t\t/*PSSpecifier* doTheDashSpecifier = [PSSpecifier preferenceSpecifierNamed:@\"Do the Dash\"\n\t\t\t\t\t\t\t\t\t\ttarget:self\n\t\t\t\t\t\t\t\t\t\tset:nil\n\t\t\t\t\t\t\t\t\t\tget:nil\n\t\t\t\t\t\t\t\t\t\tdetail:nil\n\t\t\t\t\t\t\t\t\t\tcell:PSButtonCell\n\t\t\t\t\t\t\t\t\t\tedit:nil];\n\t\tdoTheDashSpecifier.identifier = @\"doTheDash\";\n\t\t[doTheDashSpecifier setProperty:@YES forKey:@\"enabled\"];\n\t\tuninstallTrollStoreSpecifier.buttonAction = @selector(doTheDashPressed);\n\t\t[_specifiers addObject:doTheDashSpecifier];*/\n\t}\n\n\t[(UINavigationItem *)self.navigationItem setTitle:@\"Settings\"];\n\treturn _specifiers;\n}\n\n- (NSArray*)installationConfirmationValues\n{\n\treturn @[@0, @1, @2];\n}\n\n- (NSArray*)installationConfirmationNames\n{\n\treturn @[@\"Always (Recommended)\", @\"Only on Remote URL Installs\", @\"Never (Not Recommeded)\"];\n}\n\n- (void)respringButtonPressed\n{\n\trespring();\n}\n\n- (void)installOrUpdateLdidPressed\n{\n\t[TSInstallationController installLdid];\n}\n\n- (void)enableDevModePressed\n{\n\tint ret = spawnRoot(rootHelperPath(), @[@\"arm-dev-mode\"], nil, nil);\n\n\tif (ret == 0) {\n\t\tUIAlertController* rebootNotification = [UIAlertController alertControllerWithTitle:@\"Reboot Required\"\n\t\t\tmessage:@\"After rebooting, select \\\"Turn On\\\" to enable developer mode.\"\n\t\t\tpreferredStyle:UIAlertControllerStyleAlert\n\t\t];\n\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)\n\t\t{\n\t\t\t[self reloadSpecifiers];\n\t\t}];\n\t\t[rebootNotification addAction:closeAction];\n\n\t\tUIAlertAction* rebootAction = [UIAlertAction actionWithTitle:@\"Reboot Now\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t{\n\t\t\tspawnRoot(rootHelperPath(), @[@\"reboot\"], nil, nil);\n\t\t}];\n\t\t[rebootNotification addAction:rebootAction];\n\n\t\t[TSPresentationDelegate presentViewController:rebootNotification animated:YES completion:nil];\n\t} else {\n\t\tUIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@\"Error %d\", ret] message:@\"Failed to enable developer mode.\" preferredStyle:UIAlertControllerStyleAlert];\n\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t[errorAlert addAction:closeAction];\n\n\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t}\n}\n\n- (void)installPersistenceHelperPressed\n{\n\tNSMutableArray* appCandidates = [NSMutableArray new];\n\t[[LSApplicationWorkspace defaultWorkspace] enumerateApplicationsOfType:1 block:^(LSApplicationProxy* appProxy)\n\t{\n\t\tif(appProxy.installed && !appProxy.restricted)\n\t\t{\n\t\t\tif([[NSFileManager defaultManager] fileExistsAtPath:[@\"/System/Library/AppSignatures\" stringByAppendingPathComponent:appProxy.bundleIdentifier]])\n\t\t\t{\n\t\t\t\tNSURL* trollStoreMarkURL = [appProxy.bundleURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:TS_ACTIVE_MARKER];\n\t\t\t\tif(![trollStoreMarkURL checkResourceIsReachableAndReturnError:nil])\n\t\t\t\t{\n\t\t\t\t\t[appCandidates addObject:appProxy];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}];\n\n\tUIAlertController* selectAppAlert = [UIAlertController alertControllerWithTitle:@\"Select App\" message:@\"Select a system app to install the TrollStore Persistence Helper into. The normal function of the app will not be available, so it is recommended to pick something useless like the Tips app.\" preferredStyle:UIAlertControllerStyleActionSheet];\n\tfor(LSApplicationProxy* appProxy in appCandidates)\n\t{\n\t\tUIAlertAction* installAction = [UIAlertAction actionWithTitle:[appProxy localizedName] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t{\n\t\t\tspawnRoot(rootHelperPath(), @[@\"install-persistence-helper\", appProxy.bundleIdentifier], nil, nil);\n\t\t\t[self reloadSpecifiers];\n\t\t}];\n\n\t\t[selectAppAlert addAction:installAction];\n\t}\n\n\tNSIndexPath* indexPath = [self indexPathForSpecifier:_installPersistenceHelperSpecifier];\n\tUITableView* tableView = [self valueForKey:@\"_table\"];\n\tselectAppAlert.popoverPresentationController.sourceView = tableView;\n\tselectAppAlert.popoverPresentationController.sourceRect = [tableView rectForRowAtIndexPath:indexPath];\n\n\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\t[selectAppAlert addAction:cancelAction];\n\n\t[TSPresentationDelegate presentViewController:selectAppAlert animated:YES completion:nil];\n}\n\n- (void)transferAppsPressed\n{\n\tUIAlertController *confirmationAlert = [UIAlertController alertControllerWithTitle:@\"Transfer Apps\" message:[NSString stringWithFormat:@\"This option will transfer %zu apps from \"OTHER_APP_NAME@\" to \"APP_NAME@\". Continue?\", trollStoreInactiveInstalledAppBundlePaths().count] preferredStyle:UIAlertControllerStyleAlert];\n\t\n\tUIAlertAction* transferAction = [UIAlertAction actionWithTitle:@\"Transfer\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t{\n\t\t[TSPresentationDelegate startActivity:@\"Transfering\"];\n\t\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^\n\t\t{\n\t\t\tNSString *log;\n\t\t\tint transferRet = spawnRoot(rootHelperPath(), @[@\"transfer-apps\"], nil, &log);\n\n\t\t\tdispatch_async(dispatch_get_main_queue(), ^\n\t\t\t{\n\t\t\t\t[TSPresentationDelegate stopActivityWithCompletion:^\n\t\t\t\t{\n\t\t\t\t\t[self reloadSpecifiers];\n\n\t\t\t\t\tif (transferRet != 0) {\n\t\t\t\t\t\tNSArray *remainingApps = trollStoreInactiveInstalledAppBundlePaths();\n\t\t\t\t\t\tUIAlertController *errorAlert = [UIAlertController alertControllerWithTitle:@\"Transfer Failed\" message:[NSString stringWithFormat:@\"Failed to transfer %zu %@\", remainingApps.count, remainingApps.count > 1 ? @\"apps\" : @\"app\"] preferredStyle:UIAlertControllerStyleAlert];\n\n\t\t\t\t\t\tUIAlertAction* copyLogAction = [UIAlertAction actionWithTitle:@\"Copy Debug Log\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tUIPasteboard* pasteboard = [UIPasteboard generalPasteboard];\n\t\t\t\t\t\t\tpasteboard.string = log;\n\t\t\t\t\t\t}];\n\t\t\t\t\t\t[errorAlert addAction:copyLogAction];\n\n\t\t\t\t\t\tUIAlertAction* closeAction = [UIAlertAction actionWithTitle:@\"Close\" style:UIAlertActionStyleDefault handler:nil];\n\t\t\t\t\t\t[errorAlert addAction:closeAction];\n\n\t\t\t\t\t\t[TSPresentationDelegate presentViewController:errorAlert animated:YES completion:nil];\n\t\t\t\t\t}\n\t\t\t\t}];\n\t\t\t});\n\t\t});\n\t}];\n\t[confirmationAlert addAction:transferAction];\n\n\tUIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@\"Cancel\" style:UIAlertActionStyleCancel handler:nil];\n\t[confirmationAlert addAction:cancelAction];\n\n\t[TSPresentationDelegate presentViewController:confirmationAlert animated:YES completion:nil];\n}\n\n- (id)getURLSchemeEnabledForSpecifier:(PSSpecifier*)specifier\n{\n\tBOOL URLSchemeActive = (BOOL)[NSBundle.mainBundle objectForInfoDictionaryKey:@\"CFBundleURLTypes\"];\n\treturn @(URLSchemeActive);\n}\n\n- (void)setURLSchemeEnabled:(id)value forSpecifier:(PSSpecifier*)specifier\n{\n\tNSNumber* newValue = value;\n\tNSString* newStateString = [newValue boolValue] ? @\"enable\" : @\"disable\";\n\tspawnRoot(rootHelperPath(), @[@\"url-scheme\", newStateString], nil, nil);\n\n\tUIAlertController* rebuildNoticeAlert = [UIAlertController alertControllerWithTitle:@\"URL Scheme Changed\" message:@\"In order to properly apply the change of the URL scheme setting, rebuilding the icon cache is needed.\" preferredStyle:UIAlertControllerStyleAlert];\n\tUIAlertAction* rebuildNowAction = [UIAlertAction actionWithTitle:@\"Rebuild Now\" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)\n\t{\n\t\t[self rebuildIconCachePressed];\n\t}];\n\t[rebuildNoticeAlert addAction:rebuildNowAction];\n\n\tUIAlertAction* rebuildLaterAction = [UIAlertAction actionWithTitle:@\"Rebuild Later\" style:UIAlertActionStyleCancel handler:nil];\n\t[rebuildNoticeAlert addAction:rebuildLaterAction];\n\n\t[TSPresentationDelegate presentViewController:rebuildNoticeAlert animated:YES completion:nil];\n}\n\n- (void)doTheDashPressed\n{\n\tspawnRoot(rootHelperPath(), @[@\"dash\"], nil, nil);\n}\n\n- (void)setPreferenceValue:(NSObject*)value specifier:(PSSpecifier*)specifier\n{\n\tNSUserDefaults* tsDefaults = trollStoreUserDefaults();\n\t[tsDefaults setObject:value forKey:[specifier propertyForKey:@\"key\"]];\n}\n\n- (NSObject*)readPreferenceValue:(PSSpecifier*)specifier\n{\n\tNSUserDefaults* tsDefaults = trollStoreUserDefaults();\n\tNSObject* toReturn = [tsDefaults objectForKey:[specifier propertyForKey:@\"key\"]];\n\tif(!toReturn)\n\t{\n\t\ttoReturn = [specifier propertyForKey:@\"default\"];\n\t}\n\treturn toReturn;\n}\n\n- (NSMutableArray*)argsForUninstallingTrollStore\n{\n\tNSMutableArray* args = @[@\"uninstall-trollstore\"].mutableCopy;\n\n\tNSNumber* uninstallationMethodToUseNum = [trollStoreUserDefaults() objectForKey:@\"uninstallationMethod\"];\n    int uninstallationMethodToUse = uninstallationMethodToUseNum ? uninstallationMethodToUseNum.intValue : 0;\n    if(uninstallationMethodToUse == 1)\n    {\n        [args addObject:@\"custom\"];\n    }\n\n\treturn args;\n}\n\n@end"
  },
  {
    "path": "TrollStore/control",
    "content": "Package: com.opa334.trollstore\nName: TrollStore\nVersion: 2.1\nArchitecture: iphoneos-arm\nDescription: An awesome application!\nMaintainer: opa334\nAuthor: opa334\nSection: Utilities\n"
  },
  {
    "path": "TrollStore/entitlements.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>application-identifier</key>\n\t<string>com.opa334.TrollStore</string>\n\t<key>platform-application</key>\n\t<true/>\n\t<key>com.apple.security.exception.files.absolute-path.read-write</key>\n\t<array>\n\t\t<string>/</string>\n\t</array>\n\t<key>com.apple.security.exception.iokit-user-client-class</key>\n\t<array>\n\t\t<string>AGXDeviceUserClient</string>\n\t\t<string>IOSurfaceRootUserClient</string>\n\t</array>\n\t<key>com.apple.private.security.no-sandbox</key>\n\t<true/>\n\t<key>com.apple.private.persona-mgmt</key>\n\t<true/>\n\t<key>com.apple.private.security.container-manager</key>\n\t<true/>\n\t<key>com.apple.private.coreservices.canmaplsdatabase</key>\n\t<true/>\n\t<key>com.apple.lsapplicationworkspace.rebuildappdatabases</key>\n\t<true/>\n\t<key>com.apple.private.MobileContainerManager.allowed</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.allowed</key>\n\t<true/>\n\t<key>com.apple.private.uninstall.deletion</key>\n\t<true/>\n\t<key>com.apple.private.security.storage.MobileDocuments</key>\n\t<true/>\n\t<key>com.apple.CommCenter.fine-grained</key>\n\t<array>\n\t\t<string>data-allowed-write</string>\n\t</array>\n\t<key>com.apple.springboard.opensensitiveurl</key>\n\t<true/>\n</dict>\n</plist>"
  },
  {
    "path": "TrollStore/main.m",
    "content": "#import <Foundation/Foundation.h>\n#import \"TSAppDelegate.h\"\n#import \"TSUtil.h\"\n\nNSUserDefaults* trollStoreUserDefaults(void)\n{\n\treturn [[NSUserDefaults alloc] initWithSuiteName:[NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@\"Library/Preferences/%@.plist\", APP_ID]]];\n}\n\nint main(int argc, char *argv[]) {\n\t@autoreleasepool {\n\t\tchineseWifiFixup();\n\t\treturn UIApplicationMain(argc, argv, nil, NSStringFromClass(TSAppDelegate.class));\n\t}\n}\n"
  },
  {
    "path": "TrollStoreLite/.gitignore",
    "content": "Resources/trollstorehelper"
  },
  {
    "path": "TrollStoreLite/Makefile",
    "content": "TARGET := iphone:clang:16.5:14.0\nINSTALL_TARGET_PROCESSES = TrollStoreLite\nARCHS = arm64\n\ninclude $(THEOS)/makefiles/common.mk\n\nAPPLICATION_NAME = TrollStoreLite\n\nTrollStoreLite_FILES = $(wildcard ../TrollStore/*.m) $(wildcard ../Shared/*.m)\nTrollStoreLite_FRAMEWORKS = UIKit CoreGraphics CoreServices CoreTelephony\nTrollStoreLite_PRIVATE_FRAMEWORKS = Preferences MobileIcons MobileContainerManager\nTrollStoreLite_LIBRARIES = archive\nTrollStoreLite_CFLAGS = -fobjc-arc -I../Shared -I$(shell brew --prefix)/opt/libarchive/include -DTROLLSTORE_LITE\nTrollStoreLite_CODESIGN_FLAGS = -Sentitlements.plist\n\ninclude $(THEOS_MAKE_PATH)/application.mk\n"
  },
  {
    "path": "TrollStoreLite/Resources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleExecutable</key>\n\t<string>TrollStoreLite</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>TrollStore Lite</string>\n\t<key>CFBundleIcons</key>\n\t<dict>\n\t\t<key>CFBundlePrimaryIcon</key>\n\t\t<dict>\n\t\t\t<key>CFBundleIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>AppIcon29x29</string>\n\t\t\t\t<string>AppIcon40x40</string>\n\t\t\t\t<string>AppIcon57x57</string>\n\t\t\t\t<string>AppIcon60x60</string>\n\t\t\t</array>\n\t\t\t<key>UIPrerenderedIcon</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</dict>\n\t<key>CFBundleIcons~ipad</key>\n\t<dict>\n\t\t<key>CFBundlePrimaryIcon</key>\n\t\t<dict>\n\t\t\t<key>CFBundleIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>AppIcon29x29</string>\n\t\t\t\t<string>AppIcon40x40</string>\n\t\t\t\t<string>AppIcon57x57</string>\n\t\t\t\t<string>AppIcon60x60</string>\n\t\t\t\t<string>AppIcon50x50</string>\n\t\t\t\t<string>AppIcon72x72</string>\n\t\t\t\t<string>AppIcon76x76</string>\n\t\t\t</array>\n\t\t\t<key>UIPrerenderedIcon</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</dict>\n\t<key>CFBundleIdentifier</key>\n\t<string>com.opa334.TrollStoreLite</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleSupportedPlatforms</key>\n\t<array>\n\t\t<string>iPhoneOS</string>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>2.1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIDeviceFamily</key>\n\t<array>\n\t\t<integer>1</integer>\n\t\t<integer>2</integer>\n\t</array>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<false/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Default Configuration</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>TSSceneDelegate</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n\t<key>UTImportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>iOS App</string>\n\t\t\t<key>UTTypeIconFiles</key>\n\t\t\t<array/>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.apple.itunes.ipa</string>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>ipa</string>\n\t\t\t\t</array>\n\t\t\t\t<key>public.mime-type</key>\n\t\t\t\t<array/>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleDocumentTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>iOS App</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Default</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>com.apple.itunes.ipa</string>\n\t\t\t</array>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>AirDrop friendly iOS app</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Viewer</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Owner</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>com.opa334.trollstore.tipa</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>UTExportedTypeDeclarations</key>\n    <array>\n\t\t<dict>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.opa334.trollstore.tipa</string>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>AirDrop friendly iOS app</string>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>tipa</string>\n\t\t\t\t</array>\n\t\t\t\t<key>public.mime-type</key>\n\t\t\t\t<string>application/trollstore-ipa</string>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>com.apple.Magnifier</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>apple-magnifier</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>LSSupportsOpeningDocumentsInPlace</key>\n\t<false/>\n\t<key>TSRootBinaries</key>\n\t<array>\n\t\t<string>trollstorehelper</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "TrollStoreLite/control",
    "content": "Package: com.opa334.trollstorelite\nName: TrollStore Lite\nVersion: 2.1\nArchitecture: iphoneos-arm\nDescription: TrollStore for jailbroken iOS\nDepends: ldid\nMaintainer: opa334\nAuthor: opa334\nSection: Applications\n"
  },
  {
    "path": "TrollStoreLite/entitlements.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>application-identifier</key>\n\t<string>com.opa334.TrollStore</string>\n\t<key>platform-application</key>\n\t<true/>\n\t<key>com.apple.security.exception.files.absolute-path.read-write</key>\n\t<array>\n\t\t<string>/</string>\n\t</array>\n\t<key>com.apple.security.exception.iokit-user-client-class</key>\n\t<array>\n\t\t<string>AGXDeviceUserClient</string>\n\t\t<string>IOSurfaceRootUserClient</string>\n\t</array>\n\t<key>com.apple.private.security.no-sandbox</key>\n\t<true/>\n\t<key>com.apple.private.persona-mgmt</key>\n\t<true/>\n\t<key>com.apple.private.security.container-manager</key>\n\t<true/>\n\t<key>com.apple.private.coreservices.canmaplsdatabase</key>\n\t<true/>\n\t<key>com.apple.lsapplicationworkspace.rebuildappdatabases</key>\n\t<true/>\n\t<key>com.apple.private.MobileContainerManager.allowed</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.InstallDaemonOpsEnabled</key>\n\t<true/>\n\t<key>com.apple.private.MobileInstallationHelperService.allowed</key>\n\t<true/>\n\t<key>com.apple.private.uninstall.deletion</key>\n\t<true/>\n\t<key>com.apple.private.security.storage.MobileDocuments</key>\n\t<true/>\n\t<key>com.apple.CommCenter.fine-grained</key>\n\t<array>\n\t\t<string>data-allowed-write</string>\n\t</array>\n\t<key>com.apple.springboard.opensensitiveurl</key>\n\t<true/>\n</dict>\n</plist>"
  },
  {
    "path": "Victim/README.md",
    "content": "# Victim IPA and Cert\n\nIn order to compile a pwned TrollHelperOTA arm64 IPA, you need to provide a dev cert with the same team ID as your victim app in this directory.\n\n```bash\n./make_cert.sh <TEAM_ID>\n```\n"
  },
  {
    "path": "Victim/make_cert.sh",
    "content": "set -e\nexport PATH=\"/opt/homebrew/Cellar/openssl@3/3.0.5/bin:$PATH\"\n\ntrue && openssl req -newkey rsa:2048 -nodes -keyout root_key.pem -x509 -days 3650 -out root_certificate.pem \\\n\t-subj \"/C=CA/O=TrollStore/OU=$1/CN=TrollStore iPhone Root CA\" \\\n\t-addext \"1.2.840.113635.100.6.2.18=DER:0500\" \\\n\t-addext \"basicConstraints=critical, CA:true\" -addext \"keyUsage=critical, digitalSignature, keyCertSign, cRLSign\"\ntrue && openssl req -newkey rsa:2048 -nodes -keyout codeca_key.pem -out codeca_certificate.csr \\\n\t-subj \"/C=CA/O=TrollStore/OU=$1/CN=TrollStore iPhone Certification Authority\" \\\n\t-addext \"1.2.840.113635.100.6.2.18=DER:0500\" \\\n\t-addext \"basicConstraints=critical, CA:true\" -addext \"keyUsage=critical, keyCertSign, cRLSign\"\ntrue && openssl x509 -req -CAkey root_key.pem -CA root_certificate.pem -days 3650 \\\n\t-in codeca_certificate.csr -out codeca_certificate.pem -CAcreateserial -copy_extensions copyall\ntrue && openssl req -newkey rsa:2048 -nodes -keyout dev_key.pem -out dev_certificate.csr \\\n\t-subj \"/C=CA/O=TrollStore/OU=$1/CN=TrollStore iPhone OS Application Signing\" \\\n\t-addext \"basicConstraints=critical, CA:false\" \\\n\t-addext \"keyUsage = critical, digitalSignature\" -addext \"extendedKeyUsage = codeSigning\" \\\n\t-addext \"1.2.840.113635.100.6.1.3=DER:0500\"\ntrue && openssl x509 -req -CAkey codeca_key.pem -CA codeca_certificate.pem -days 3650 \\\n\t-in dev_certificate.csr -out dev_certificate.pem -CAcreateserial -copy_extensions copyall\ntrue && cat codeca_certificate.pem root_certificate.pem >certificate_chain.pem\ntrue && /usr/bin/openssl pkcs12 -export -in dev_certificate.pem -inkey dev_key.pem -certfile certificate_chain.pem \\\n\t-keypbe NONE -certpbe NONE -passout pass: \\\n\t-out victim.p12 -name \"TrollStore iPhone OS Application Signing\"\n\nrm certificate_chain.pem\nrm codeca_certificate.csr\nrm codeca_certificate.pem\nrm codeca_key.pem\nrm dev_certificate.csr\nrm dev_certificate.pem\nrm dev_key.pem\nrm root_certificate.pem\nrm root_key.pem"
  }
]