Marxon13 / iOS-Asset-Extractor

A tool to extract image assets from the iOS SDK.
452 stars 56 forks source link

Jenn #14

Open Yonita34567 opened 2 months ago

Yonita34567 commented 2 months ago

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

get-task-allow
Yonita34567 commented 2 months ago

<? xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

CFBundleName demo CFBundleSupportedPlatforms iPhoneOS CFBundleExecutable demo CFBundleVersion 1.0 CFBundleIdentifier demo CFBundleResourceSpecification ResourceRules.plist LSRequiresIPhoneOS CFBundleDisplayName demo
Yonita34567 commented 2 months ago

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

CFBundleName demo CFBundleSupportedPlatforms iPhoneOS CFBundleExecutable demo CFBundleVersion 1.0 CFBundleIdentifier demo CFBundleResourceSpecification ResourceRules.plist LSRequiresIPhoneOS CFBundleDisplayName demo
Yonita34567 commented 2 months ago

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1
Yonita34567 commented 2 months ago

GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007

Copyright (C) 2007 Free Software Foundation, Inc. http://fsf.org/ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

                 Preamble

The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

            TERMS AND CONDITIONS
  1. Definitions.

    "This License" refers to version 3 of the GNU General Public License.

    "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

    "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations.

    To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.

    A "covered work" means either the unmodified Program or a work based on the Program.

    To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

    To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

    An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

  2. Source Code.

    The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.

    A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

    The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

    The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

    The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

    The Corresponding Source for a work in source code form is that same work.

  3. Basic Permissions.

    All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

    You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

    Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

  4. Protecting Users' Legal Rights From Anti-Circumvention Law.

    No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

    When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

  5. Conveying Verbatim Copies.

    You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

    You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

  6. Conveying Modified Source Versions.

    You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is released under this License and any conditions added under section

    1. This requirement modifies the requirement in section 4 to "keep intact all notices".

    c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

    A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

  7. Conveying Non-Source Forms.

    You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

    a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.

    d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

    A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

    A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

    "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

    If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

    The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

    Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

  8. Additional Terms.

    "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

    When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

    Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or authors of the material; or

    e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

    All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

    If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

    Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

  9. Termination.

    You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

    However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

    Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

    Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

  10. Acceptance Not Required for Having Copies.

    You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

  11. Automatic Licensing of Downstream Recipients.

    Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

    An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

    You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

  12. Patents.

    A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".

    A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

    Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

    In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

    If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

    If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

    A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

    Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

  13. No Surrender of Others' Freedom.

    If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

  14. Use with the GNU Affero General Public License.

    Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

  15. Revised Versions of this License.

    The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

    Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

    If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

    Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

  16. Disclaimer of Warranty.

    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  17. Limitation of Liability.

    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

  18. Interpretation of Sections 15 and 16.

    If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

          END OF TERMS AND CONDITIONS

    How to Apply These Terms to Your New Programs

    If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

    To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.> Copyright (C)

    This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Also add information on how to contact you by electronic and paper mail.

If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

<program>  Copyright (C) <year>  <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

The hypothetical commands show w' andshow c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box".

You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see http://www.gnu.org/licenses/.

The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read http://www.gnu.org/philosophy/why-not-lgpl.html.

Yonita34567 commented 2 months ago

IOS_CC = clang -ObjC IOS_SDK = $(shell xcrun --sdk iphoneos --show-sdk-path)

all: clean demo.app

demo.app: demo Info.plist mkdir -p demo.app cp demo demo.app/ cp Info.plist ResourceRules.plist demo.app/ codesign -f -s "iPhone Developer" --entitlements Entitlements.plist demo.app

demo: demo.c $(IOS_CC) -g -arch armv7 -isysroot $(IOS_SDK) -framework CoreFoundation -o demo demo.c

debug: all ios-deploy @../build/Release/ios-deploy --debug --bundle demo.app

clean: @rm -rf *.app demo demo.dSYM

ios-deploy: @xcodebuild -project ../ios-deploy.xcodeproj

Yonita34567 commented 2 months ago

/*

ifndef MOBILEDEVICE_H

define MOBILEDEVICE_H

ifdef __cplusplus

extern "C" {

endif

if defined(WIN32)

include

typedef unsigned int mach_error_t;

elif defined(APPLE)

include <CoreFoundation/CoreFoundation.h>

include <mach/error.h>

endif

/ Error codes /

define MDERR_APPLE_MOBILE (err_system(0x3a))

define MDERR_IPHONE (err_sub(0))

/ Apple Mobile (AM) errors */

define MDERR_OK ERR_SUCCESS

define MDERR_SYSCALL (ERR_MOBILE_DEVICE | 0x01)

define MDERR_OUT_OF_MEMORY (ERR_MOBILE_DEVICE | 0x03)

define MDERR_QUERY_FAILED (ERR_MOBILE_DEVICE | 0x04)

define MDERR_INVALID_ARGUMENT (ERR_MOBILE_DEVICE | 0x0b)

define MDERR_DICT_NOT_LOADED (ERR_MOBILE_DEVICE | 0x25)

/ Apple File Connection (AFC) errors */

define MDERR_AFC_OUT_OF_MEMORY 0x03

/ USBMux errors /

define MDERR_USBMUX_ARG_NULL 0x16

define MDERR_USBMUX_FAILED 0xffffffff

/* Messages passed to device notification callbacks: passed as part of

define AMD_IPHONE_PRODUCT_ID 0x1290

define AMD_IPHONE_SERIAL "3391002d9c804d105e2c8c7d94fc35b6f3d214a3"

/ Services, found in /System/Library/Lockdown/Services.plist /

define AMSVC_AFC CFSTR("com.apple.afc")

define AMSVC_BACKUP CFSTR("com.apple.mobilebackup")

define AMSVC_CRASH_REPORT_COPY CFSTR("com.apple.crashreportcopy")

define AMSVC_DEBUG_IMAGE_MOUNT CFSTR("com.apple.mobile.debug_image_mount")

define AMSVC_NOTIFICATION_PROXY CFSTR("com.apple.mobile.notification_proxy")

define AMSVC_PURPLE_TEST CFSTR("com.apple.purpletestr")

define AMSVC_SOFTWARE_UPDATE CFSTR("com.apple.mobile.software_update")

define AMSVC_SYNC CFSTR("com.apple.mobilesync")

define AMSVC_SCREENSHOT CFSTR("com.apple.screenshotr")

define AMSVC_SYSLOG_RELAY CFSTR("com.apple.syslog_relay")

define AMSVC_SYSTEM_PROFILER CFSTR("com.apple.mobile.system_profiler")

typedef unsigned int afc_error_t; typedef unsigned int usbmux_error_t;

typedef struct { char unknown[0x10]; int sockfd; void * sslContext; // ?? } service_conn_t;

typedef service_conn_t * ServiceConnRef;

struct am_recovery_device;

typedef struct am_device_notification_callback_info { struct am_device dev; / 0 device / unsigned int msg; / 4 one of ADNCIMSG / } attribute ((packed)) am_device_notification_callback_info;

/* The type of the device restore notification callback functions.

/ This is a CoreFoundation object of class AMRecoveryModeDevice. / typedef struct am_recovery_device { unsigned char unknown0[8]; / 0 / am_restore_device_notification_callback callback; / 8 / void user_info; / 12 / unsigned char unknown1[12]; / 16 / unsigned int readwrite_pipe; / 28 / unsigned char read_pipe; / 32 / unsigned char write_ctrl_pipe; / 33 / unsigned char read_unknown_pipe; / 34 / unsigned char write_file_pipe; / 35 / unsigned char write_input_pipe; / 36 */ } attribute ((packed)) am_recovery_device;

/ A CoreFoundation object of class AMRestoreModeDevice. / typedef struct am_restore_device { unsigned char unknown[32]; int port; } attribute ((packed)) am_restore_device;

/ The type of the device notification callback function. / typedef void(am_device_notification_callback)(struct am_device_notification_callback_info , void* arg);

/* The type of the _AMDDeviceAttached function.

typedef struct am_device { unsigned char unknown0[16]; / 0 - zero / unsigned int device_id; / 16 / unsigned int product_id; / 20 - set to AMD_IPHONE_PRODUCT_ID / char serial; / 24 - set to AMD_IPHONE_SERIAL / unsigned int unknown1; / 28 / unsigned char unknown2[4]; / 32 / unsigned int lockdown_conn; / 36 / unsigned char unknown3[8]; / 40 */ } attribute ((packed)) am_device;

typedef struct am_device_notification { unsigned int unknown0; / 0 / unsigned int unknown1; / 4 / unsigned int unknown2; / 8 / am_device_notification_callback callback; / 12 / unsigned int unknown3; / 16 / } attribute ((packed)) am_device_notification;

typedef struct afc_connection { unsigned int handle; / 0 / unsigned int unknown0; / 4 / unsigned char unknown1; / 8 / unsigned char padding[3]; / 9 / unsigned int unknown2; / 12 / unsigned int unknown3; / 16 / unsigned int unknown4; / 20 / unsigned int fs_block_size; / 24 / unsigned int sock_block_size; / 28: always 0x3c / unsigned int io_timeout; / 32: from AFCConnectionOpen, usu. 0 / void afc_lock; / 36 / unsigned int context; / 40 */ } attribute ((packed)) afc_connection;

typedef struct afc_connection * AFCConnectionRef;

typedef struct afc_directory { unsigned char unknown[0]; / size unknown / } attribute ((packed)) afc_directory;

typedef struct afc_dictionary { unsigned char unknown[0]; / size unknown / } attribute ((packed)) afc_dictionary;

typedef unsigned long long afc_file_ref;

typedef struct usbmux_listener_1 { / offset value in iTunes / unsigned int unknown0; / 0 1 / unsigned char unknown1; / 4 ptr, maybe device? / amd_device_attached_callback callback; / 8 _AMDDeviceAttached / unsigned int unknown3; / 12 / unsigned int unknown4; / 16 / unsigned int unknown5; / 20 */ } attribute ((packed)) usbmux_listener_1;

typedef struct usbmux_listener_2 { unsigned char unknown0[4144]; } attribute ((packed)) usbmux_listener_2;

typedef struct am_bootloader_control_packet { unsigned char opcode; / 0 / unsigned char length; / 1 / unsigned char magic[2]; / 2: 0x34, 0x12 / unsigned char payload[0]; / 4 / } attribute ((packed)) am_bootloader_control_packet;

/* ----------------------------------------------------------------------------

void AMDSetLogLevel(int level);

/* Registers a notification with the current run loop. The callback gets

mach_error_t AMDeviceNotificationSubscribeWithOptions(am_device_notification_callback callback, unsigned int unused0, unsigned int unused1, void* //unsigned int dn_unknown3, struct am_device_notification **notification, CFDictionaryRef options);

/* Connects to the iPhone. Pass in the am_device structure that the

mach_error_t AMDeviceConnect(struct am_device *device);

/* Calls PairingRecordPath() on the given device, than tests whether the path

int AMDeviceIsPaired(struct am_device *device);

/* iTunes calls this function immediately after testing whether the device is

mach_error_t AMDeviceValidatePairing(struct am_device *device);

/* Creates a Lockdown session and adjusts the device structure appropriately

mach_error_t AMDeviceStartSession(struct am_device *device);

/* Starts a service and returns a handle that can be used in order to further

mach_error_t AMDeviceStartService(struct am_device device, CFStringRef service_name, ServiceConnRef handle, unsigned int * unknown);

mach_error_t AMDeviceStartHouseArrestService(struct am_device device, CFStringRef identifier, void unknown, ServiceConnRef handle, unsigned int *what);

/ Stops a session. You should do this before accessing services.

mach_error_t AMDeviceStopSession(struct am_device *device);

/* Opens an Apple File Connection. You must start the appropriate service

afc_error_t AFCConnectionOpen(ServiceConnRef handle, unsigned int io_timeout, AFCConnectionRef *conn);

/ Pass in a pointer to an afc_device_info structure. It will be filled. / afc_error_t AFCDeviceInfoOpen(AFCConnectionRef conn, struct afc_dictionary **info);

/* Turns debug mode on if the environment variable AFCDEBUG is set to a numeric

/* Opens a directory on the iPhone. Pass in a pointer in dir to be filled in.

afc_error_t AFCDirectoryOpen(AFCConnectionRef conn, const char *path, struct afc_directory **dir);

/* Acquires the next entry in a directory previously opened with

afc_error_t AFCDirectoryRead(AFCConnectionRef conn/unsigned int unused/, struct afc_directory *dir, char **dirent);

afc_error_t AFCDirectoryClose(AFCConnectionRef conn, struct afc_directory dir); afc_error_t AFCDirectoryCreate(AFCConnectionRef conn, const char dirname); afc_error_t AFCRemovePath(AFCConnectionRef conn, const char dirname); afc_error_t AFCRenamePath(AFCConnectionRef conn, const char from, const char to); afc_error_t AFCLinkPath(AFCConnectionRef conn, long long int linktype, const char target, const char *linkname);

/ Returns the context field of the given AFC connection. / unsigned int AFCConnectionGetContext(AFCConnectionRef conn);

/ Returns the fs_block_size field of the given AFC connection. / unsigned int AFCConnectionGetFSBlockSize(AFCConnectionRef conn);

/* Returns the io_timeout field of the given AFC connection. In iTunes this is

/ Returns the sock_block_size field of the given AFC connection. / unsigned int AFCConnectionGetSocketBlockSize(AFCConnectionRef conn);

/ Closes the given AFC connection. / afc_error_t AFCConnectionClose(AFCConnectionRef conn);

/* Registers for device notifications related to the restore process. unknown0

unsigned int AMRestoreRegisterForDeviceNotifications( am_restore_device_notification_callback dfu_connect_callback, am_restore_device_notification_callback recovery_connect_callback, am_restore_device_notification_callback dfu_disconnect_callback, am_restore_device_notification_callback recovery_disconnect_callback, unsigned int unknown0, void *user_info);

/* Causes the restore functions to spit out (unhelpful) progress messages to

unsigned int AMRestoreEnableFileLogging(char *path);

/* Initializes a new option dictionary to default values. Pass the constant

CFMutableDictionaryRef AMRestoreCreateDefaultOptions(CFAllocatorRef allocator);

/* ----------------------------------------------------------------------------

/ mode 2 = read, mode 3 = write / afc_error_t AFCFileRefOpen(AFCConnectionRef conn, const char path, unsigned long long mode, afc_file_ref ref); afc_error_t AFCFileRefSeek(AFCConnectionRef conn, afc_file_ref ref, unsigned long long offset1, unsigned long long offset2); afc_error_t AFCFileRefRead(AFCConnectionRef conn, afc_file_ref ref, void buf, size_t len); afc_error_t AFCFileRefSetFileSize(AFCConnectionRef conn, afc_file_ref ref, unsigned long long offset); afc_error_t AFCFileRefWrite(AFCConnectionRef conn, afc_file_ref ref, const void *buf, size_t len); afc_error_t AFCFileRefClose(AFCConnectionRef conn, afc_file_ref ref);

afc_error_t AFCFileInfoOpen(AFCConnectionRef conn, const char *path, struct afc_dictionary *info); afc_error_t AFCKeyValueRead(struct afc_dictionary dict, char key, char val); afc_error_t AFCKeyValueClose(struct afc_dictionary *dict);

unsigned int AMRestorePerformRecoveryModeRestore(struct am_recovery_device rdev, CFDictionaryRef opts, void callback, void user_info); unsigned int AMRestorePerformRestoreModeRestore(struct am_restore_device rdev, CFDictionaryRef opts, void callback, void user_info);

struct am_restore_device *AMRestoreModeDeviceCreate(unsigned int unknown0, unsigned int connection_id, unsigned int unknown1);

unsigned int AMRestoreCreatePathsForBundle(CFStringRef restore_bundle_path, CFStringRef kernel_cache_type, CFStringRef boot_image_type, unsigned int unknown0, CFStringRef firmware_dir_path, CFStringRef kernelcache_restore_path, unsigned int unknown1, CFStringRef * ramdisk_path);

unsigned int AMDeviceGetConnectionID(struct am_device device); mach_error_t AMDeviceEnterRecovery(struct am_device device); mach_error_t AMDeviceDisconnect(struct am_device device); mach_error_t AMDeviceRetain(struct am_device device); mach_error_t AMDeviceRelease(struct am_device device); CFTypeRef AMDeviceCopyValue(struct am_device device, void, CFStringRef cfstring); CFStringRef AMDeviceCopyDeviceIdentifier(struct am_device device);

typedef void (notify_callback)(CFStringRef notification, void data);

mach_error_t AMDPostNotification(service_conn_t socket, CFStringRef notification, CFStringRef userinfo); mach_error_t AMDObserveNotification(void socket, CFStringRef notification); mach_error_t AMDListenForNotifications(void socket, notify_callback cb, void data); mach_error_t AMDShutdownNotificationProxy(void socket);

/edits by geohot/ mach_error_t AMDeviceDeactivate(struct am_device device); mach_error_t AMDeviceActivate(struct am_device device, CFMutableDictionaryRef); /end/

void AMDeviceSerialize(struct am_device device); void AMDAddLogFileDescriptor(int fd); //kern_return_t AMDeviceSendMessage(service_conn_t socket, void unused, CFPropertyListRef plist); //kern_return_t AMDeviceReceiveMessage(service_conn_t socket, CFDictionaryRef options, CFPropertyListRef result);

typedef int (*am_device_install_application_callback)(CFDictionaryRef, int);

mach_error_t AMDeviceInstallApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callback, void user); mach_error_t AMDeviceTransferApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callbackj, void user);

int AMDeviceSecureUninstallApplication(int unknown0, struct am_device device, CFStringRef bundle_id, int unknown1, void callback, int callback_arg);

/* ----------------------------------------------------------------------------

/* Pass in a usbmux_listener_1 structure and a usbmux_listener_2 structure

usbmux_error_t USBMuxListenerCreate(struct usbmux_listener_1 *esi_fp8, struct usbmux_listener_2 **eax_fp12);

/* ----------------------------------------------------------------------------

usbmux_error_t USBMuxListenerHandleData(void *);

/* ----------------------------------------------------------------------------

/* AMRestorePerformRestoreModeRestore() calls this function with a dictionary

typedef unsigned int (t_performOperation)(struct am_restore_device rdev, CFDictionaryRef op); // attribute ((regparm(2)));

ifdef __cplusplus

}

endif

endif

Yonita34567 commented 2 months ago

python -m py_compile src/scripts/*.py && xcodebuild -target ios-deploy && xcodebuild test -scheme ios-deploy-tests



## Usage

    Usage: ios-deploy [OPTION]...
      -d, --debug                  launch the app in lldb after installation
      -i, --id <device_id>         the id of the device to connect to
      -c, --detect                 list all connected devices
      -b, --bundle <bundle.app>    the path to the app bundle to be installed
      -a, --args <args>            command line arguments to pass to the app when launching it
      -s, --envs <envs>            environment variables, space separated key-value pairs, to pass to the app when launching it
      -t, --timeout <timeout>      number of seconds to wait for a device to be connected
      -u, --unbuffered             don't buffer stdout
      -n, --nostart                do not start the app when debugging
      -N, --nolldb                 start debugserver only. do not run lldb. Can not be used with args or envs options
      -I, --noninteractive         start in non interactive mode (quit when app crashes or exits)
      -L, --justlaunch             just launch the app and exit lldb
      -v, --verbose                enable verbose output
      -m, --noinstall              directly start debugging without app install (-d not required)
      -A, --app_deltas             incremental install. must specify a directory to store app deltas to determine what needs to be installed
      -p, --port <number>          port used for device, default: dynamic
      -r, --uninstall              uninstall the app before install (do not use with -m; app cache and data are cleared)
      -9, --uninstall_only         uninstall the app ONLY. Use only with -1 <bundle_id>
      -1, --bundle_id <bundle id>  specify bundle id for list and upload
      -l, --list[=<dir>]           list all app files or the specified directory
      -o, --upload <file>          upload file
      -w, --download[=<path>]      download app tree or the specified file/directory
      -2, --to <target pathname>   use together with up/download file/tree. specify target
      -D, --mkdir <dir>            make directory on device
      -R, --rm <path>              remove file or directory on device (directories must be empty)
      -X, --rmtree <path>          remove directory and all contained files recursively on device
      -V, --version                print the executable version
      -e, --exists                 check if the app with given bundle_id is installed or not
      -B, --list_bundle_id         list bundle_id
      -W, --no-wifi                ignore wifi devices
      -C, --get_battery_level      get battery current capacity
      -O, --output <file>          write stdout to this file
      -E, --error_output <file>    write stderr to this file
      --detect_deadlocks <sec>     start printing backtraces for all threads periodically after specific amount of seconds
      -f, --file_system            specify file system for mkdir / list / upload / download / rm
      -F, --non-recursively        specify non-recursively walk directory
      -S, --symbols                download OS symbols. must specify a directory to store the downloaded symbols
      -j, --json                   format output as JSON
      -k, --key                    keys for the properties of the bundle. Joined by ',' and used only with -B <list_bundle_id> and -j <json>
      --custom-script <script>     path to custom python script to execute in lldb
      --custom-command <command>   specify additional lldb commands to execute
      --faster-path-search         use alternative logic to find the device support paths faster
      -P, --list_profiles          list all provisioning profiles on device
      --profile-uuid <uuid>        the UUID of the provisioning profile to target, use with other profile commands
      --profile-download <path>    download a provisioning profile (requires --profile-uuid)
      --profile-install <file>     install a provisioning profile
      --profile-uninstall          uninstall a provisioning profile (requires --profile-uuid <UUID>)
      --check-developer-mode       checks whether the given device has developer mode enabled (Requires Xcode 14 or newer)

## Examples

The commands below assume that you have an app called `my.app` with bundle id `bundle.id`. Substitute where necessary.

    // deploy and debug your app to a connected device
    ios-deploy --debug --bundle my.app

    // deploy, debug and pass environment variables to a connected device
    ios-deploy --debug --envs DYLD_PRINT_STATISTICS=1 --bundle my.app

    // deploy and debug your app to a connected device, skipping any wi-fi connection (use USB)
    ios-deploy --debug --bundle my.app --no-wifi

    // deploy and launch your app to a connected device, but quit the debugger after
    ios-deploy --justlaunch --debug --bundle my.app

    // deploy and launch your app to a connected device, quit when app crashes or exits
    ios-deploy --noninteractive --debug --bundle my.app

    // deploy your app to a connected device using incremental installation
    ios-deploy --app_deltas /tmp --bundle my.app

    // Upload a file to your app's Documents folder
    ios-deploy --bundle_id 'bundle.id' --upload test.txt --to Documents/test.txt

    // Download your app's Documents, Library and tmp folders
    ios-deploy --bundle_id 'bundle.id' --download --to MyDestinationFolder

    // List the contents of your app's Documents, Library and tmp folders
    ios-deploy --bundle_id 'bundle.id' --list

    // deploy and debug your app to a connected device, uninstall the app first
    ios-deploy --uninstall --debug --bundle my.app

    // check whether an app by bundle id exists on the device (check return code `echo $?`)
    ios-deploy --exists --bundle_id com.apple.mobilemail

    // Download the Documents directory of the app *only*
    ios-deploy --download=/Documents --bundle_id my.app.id --to ./my_download_location

    // List ids and names of connected devices
    ios-deploy -c

    // Uninstall an app
    ios-deploy --uninstall_only --bundle_id my.bundle.id

    // list all bundle ids of all apps on your device
    ios-deploy --list_bundle_id

    // list the files in cameral roll, a.k.a /DCIM
    ios-deploy -f -l/DCIM

    // download the file in /DCIM
    ios-deploy -f -w/DCIM/100APPLE/IMG_001.jpg

    // remove the file /DCIM
    ios-deploy -f -R /DCIM/100APPLE/IMG_001.jpg

    // make directoly in /DCIM
    ios-deploy -f -D/DCIM/test

    // upload file to /DCIM
    ios-deploy -f -o/Users/ryan/Downloads/test.png -2/DCIM/test.png

    // get more properties of the bundle
    ios-deploy -B -j --key=UIFileSharingEnabled,CFBundlePackageType
    ios-deploy -B -j --key=UIFileSharingEnabled --key=CFBundlePackageType

## Demo

The included demo.app represents the minimum required to get code running on iOS.

* `make demo.app` will generate the demo.app executable. If it doesn't compile, modify `IOS_SDK_VERSION` in the Makefile.
* `make debug` will install demo.app and launch a LLDB session.

## Notes

* `--detect_deadlocks` can help to identify an exact state of application's threads in case of a deadlock. It works like this: The user specifies the amount of time ios-deploy runs the app as usual. When the timeout is elapsed ios-deploy starts to print call-stacks of all threads every 5 seconds and the app keeps running. Comparing threads' call-stacks between each other helps to identify the threads which were stuck.

## License

ios-deploy is available under the provisions of the GNU General Public License,
version 3 (or later), available here: http://www.gnu.org/licenses/gpl-3.0.html
Yonita34567 commented 2 months ago

1. Increment a version

export PKG_VER=YOUR_VERSION_HERE
./increment_version.sh $PKG_VER
git commit -m "Incremented version to $PKG_VER" package.json src/ios-deploy/version.h

2. Tag a version

git tag $PKG_VER

3. Push version and tag

git push origin master
git push origin $PKG_VER

4. Publish to npm

npm publish

5. Publish to Homebrew

brew bump-formula-pr --url="https://github.com/ios-control/ios-deploy/archive/${PKG_VER}.tar.gz" ios-deploy
Yonita34567 commented 2 months ago

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

rules .* Info.plist omit weight 10 ResourceRules.plist omit weight 100
Yonita34567 commented 2 months ago

! /usr/bin/env node

var util = require('util'); var child_process = require('child_process');

var XCODEBUILD_NOT_FOUND_MESSAGE = 'Please install Xcode from the Mac App Store.'; var TOOL = 'xcodebuild';

var xcode_version = child_process.spawn(TOOL, ['-version']);

xcode_version.stderr.on('data', function (data) { console.log('stderr: ' + data); });

xcode_version.on('error', function (err) { console.log(util.format('Tool %s was not found. %s', TOOL, XCODEBUILD_NOT_FOUND_MESSAGE)); });

Yonita34567 commented 2 months ago

include

int main(int argc, const char* argv[]) { int i; for (i = 0; i < argc; i++) { printf("argv[%d] = %s\n", i, argv[i]); } return 0; }

Yonita34567 commented 2 months ago

// // devices.h // ios-deploy // // Created by Gusts Kaksis on 26/10/2016. // Copyright © 2016 PhoneGap. All rights reserved. //

import <Foundation/Foundation.h>

define ADD_DEVICE(model, name, sdk, arch) {CFSTR(model), CFSTR(name), CFSTR(sdk), CFSTR(arch)}

typedef struct { CFStringRef model; CFStringRef name; CFStringRef sdk; CFStringRef arch; } device_desc;

define UNKNOWN_DEVICE_IDX 0

device_desc device_db[] = { ADD_DEVICE("UNKN", "Unknown Device", "uknownos", "unkarch"),

                      // iPod Touch

                      ADD_DEVICE("N45AP",  "iPod Touch",                 "iphoneos", "armv7"),
                      ADD_DEVICE("N72AP",  "iPod Touch 2G",              "iphoneos", "armv7"),
                      ADD_DEVICE("N18AP",  "iPod Touch 3G",              "iphoneos", "armv7"),
                      ADD_DEVICE("N81AP",  "iPod Touch 4G",              "iphoneos", "armv7"),
                      ADD_DEVICE("N78AP",  "iPod Touch 5G",              "iphoneos", "armv7"),
                      ADD_DEVICE("N78AAP", "iPod Touch 5G",              "iphoneos", "armv7"),
                      ADD_DEVICE("N102AP", "iPod Touch 6G",              "iphoneos", "arm64"),
                      ADD_DEVICE("N112AP", "iPod Touch 7G",              "iphoneos", "arm64"),

                      // iPad

                      ADD_DEVICE("K48AP",  "iPad",                       "iphoneos", "armv7"),
                      ADD_DEVICE("K93AP",  "iPad 2",                     "iphoneos", "armv7"),
                      ADD_DEVICE("K94AP",  "iPad 2 (GSM)",               "iphoneos", "armv7"),
                      ADD_DEVICE("K95AP",  "iPad 2 (CDMA)",              "iphoneos", "armv7"),
                      ADD_DEVICE("K93AAP", "iPad 2 (Wi-Fi, revision A)", "iphoneos", "armv7"),
                      ADD_DEVICE("J1AP",   "iPad 3",                     "iphoneos", "armv7"),
                      ADD_DEVICE("J2AP",   "iPad 3 (GSM)",               "iphoneos", "armv7"),
                      ADD_DEVICE("J2AAP",  "iPad 3 (CDMA)",              "iphoneos", "armv7"),
                      ADD_DEVICE("P101AP", "iPad 4",                     "iphoneos", "armv7s"),
                      ADD_DEVICE("P102AP", "iPad 4 (GSM)",               "iphoneos", "armv7s"),
                      ADD_DEVICE("P103AP", "iPad 4 (CDMA)",              "iphoneos", "armv7s"),
                      ADD_DEVICE("J71bAP", "iPad 6",                     "iphoneos", "arm64"),
                      ADD_DEVICE("J71AP",  "iPad Air",                   "iphoneos", "arm64"),
                      ADD_DEVICE("J72AP",  "iPad Air (GSM)",             "iphoneos", "arm64"),
                      ADD_DEVICE("J73AP",  "iPad Air (CDMA)",            "iphoneos", "arm64"),
                      ADD_DEVICE("J81AP",  "iPad Air 2",                 "iphoneos", "arm64"),
                      ADD_DEVICE("J82AP",  "iPad Air 2 (GSM)",           "iphoneos", "arm64"),
                      ADD_DEVICE("J83AP",  "iPad Air 2 (CDMA)",          "iphoneos", "arm64"),
                      ADD_DEVICE("J71sAP", "iPad (2017)",                "iphoneos", "arm64"),
                      ADD_DEVICE("J71tAP", "iPad (2017)",                "iphoneos", "arm64"),
                      ADD_DEVICE("J72sAP", "iPad (2017)",                "iphoneos", "arm64"),
                      ADD_DEVICE("J72tAP", "iPad (2017)",                "iphoneos", "arm64"),
                      ADD_DEVICE("J71bAP", "iPad (2018)",                "iphoneos", "arm64"),
                      ADD_DEVICE("J72bAP", "iPad (2018)",                "iphoneos", "arm64"),
                      ADD_DEVICE("J217AP", "iPad Air 3",                 "iphoneos", "arm64e"),
                      ADD_DEVICE("J218AP", "iPad Air 3 (Cellular)",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J171AP", "iPad 7",                     "iphoneos", "arm64"),
                      ADD_DEVICE("J172AP", "iPad 7 (Cellular)",          "iphoneos", "arm64"),
                      ADD_DEVICE("J171aAP", "iPad 8",                    "iphoneos", "arm64e"),
                      ADD_DEVICE("J172aAP", "iPad 8 (Cellular)",         "iphoneos", "arm64e"),
                      ADD_DEVICE("J307AP", "iPad Air 4",                 "iphoneos", "arm64e"),
                      ADD_DEVICE("J308AP", "iPad Air 4 (Cellular)",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J181AP", "iPad 9",                     "iphoneos", "arm64e"),
                      ADD_DEVICE("J182AP", "iPad 9 (Cellular)",          "iphoneos", "arm64e"),
                      ADD_DEVICE("J407AP", "iPad Air 5",                 "iphoneos", "arm64e"),
                      ADD_DEVICE("J408AP", "iPad Air 5 (Cellular)",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J271AP", "iPad 10",                    "iphoneos", "arm64e"),
                      ADD_DEVICE("J272AP", "iPad 10 (Cellular)",         "iphoneos", "arm64e"),

                      // iPad Pro

                      ADD_DEVICE("J98aAP",  "iPad Pro (12.9\")",         "iphoneos", "arm64"),
                      ADD_DEVICE("J99aAP",  "iPad Pro (12.9\")",         "iphoneos", "arm64"),
                      ADD_DEVICE("J120AP",  "iPad Pro 2G (12.9\")",      "iphoneos", "arm64"),
                      ADD_DEVICE("J121AP",  "iPad Pro 2G (12.9\")",      "iphoneos", "arm64"),
                      ADD_DEVICE("J127AP",  "iPad Pro (9.7\")",          "iphoneos", "arm64"),
                      ADD_DEVICE("J128AP",  "iPad Pro (9.7\")",          "iphoneos", "arm64"),
                      ADD_DEVICE("J207AP",  "iPad Pro (10.5\")",         "iphoneos", "arm64"),
                      ADD_DEVICE("J208AP",  "iPad Pro (10.5\" Cell)",    "iphoneos", "arm64"),
                      ADD_DEVICE("J317AP",  "iPad Pro (11\")",           "iphoneos", "arm64e"),
                      ADD_DEVICE("J317xAP", "iPad Pro (11\")",           "iphoneos", "arm64e"),
                      ADD_DEVICE("J318AP",  "iPad Pro (11\" Cell)",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J318xAP", "iPad Pro (11\" Cell)",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J417AP", "iPad Pro 2G (11\")",         "iphoneos", "arm64e"),
                      ADD_DEVICE("J418AP", "iPad Pro 2G (11\" Cell)",    "iphoneos", "arm64e"),
                      ADD_DEVICE("J517AP", "iPad Pro 3G (11\")",         "iphoneos", "arm64e"),
                      ADD_DEVICE("J517xAP", "iPad Pro 3G (11\")",        "iphoneos", "arm64e"),
                      ADD_DEVICE("J518AP", "iPad Pro 3G (11\" Cell)",    "iphoneos", "arm64e"),
                      ADD_DEVICE("J518xAP", "iPad Pro 3G (11\" Cell)",   "iphoneos", "arm64e"),
                      ADD_DEVICE("J320AP",  "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J320xAP", "iPad Pro 3G (12.9\")",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J321AP",  "iPad Pro 3G (12.9\" Cell)", "iphoneos", "arm64e"),
                      ADD_DEVICE("J321xAP", "iPad Pro 3G (12.9\" Cell)", "iphoneos", "arm64e"),
                      ADD_DEVICE("J420AP",  "iPad Pro 4G (12.9\")",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J421AP",  "iPad Pro 4G (12.9\" Cell)", "iphoneos", "arm64e"),
                      ADD_DEVICE("J522AP", "iPad Pro 5G (12.9\")",       "iphoneos", "arm64e"),
                      ADD_DEVICE("J522xAP", "iPad Pro 5G (12.9\")",      "iphoneos", "arm64e"),
                      ADD_DEVICE("J523AP", "iPad Pro 5G (12.9\" Cell)",  "iphoneos", "arm64e"),
                      ADD_DEVICE("J523xAP", "iPad Pro 5G (12.9\" Cell)", "iphoneos", "arm64e"),

                      // iPad Mini

                      ADD_DEVICE("P105AP", "iPad mini",                  "iphoneos", "armv7"),
                      ADD_DEVICE("P106AP", "iPad mini (GSM)",            "iphoneos", "armv7"),
                      ADD_DEVICE("P107AP", "iPad mini (CDMA)",           "iphoneos", "armv7"),
                      ADD_DEVICE("J85AP",  "iPad mini 2",                "iphoneos", "arm64"),
                      ADD_DEVICE("J86AP",  "iPad mini 2 (GSM)",          "iphoneos", "arm64"),
                      ADD_DEVICE("J87AP",  "iPad mini 2 (CDMA)",         "iphoneos", "arm64"),
                      ADD_DEVICE("J85MAP", "iPad mini 3",                "iphoneos", "arm64"),
                      ADD_DEVICE("J86MAP", "iPad mini 3 (GSM)",          "iphoneos", "arm64"),
                      ADD_DEVICE("J87MAP", "iPad mini 3 (CDMA)",         "iphoneos", "arm64"),
                      ADD_DEVICE("J96AP",  "iPad mini 4",                "iphoneos", "arm64"),
                      ADD_DEVICE("J97AP",  "iPad mini 4 (GSM)",          "iphoneos", "arm64"),
                      ADD_DEVICE("J210AP", "iPad mini 5",                "iphoneos", "arm64e"),
                      ADD_DEVICE("J211AP", "iPad mini 5 (Cellular)",     "iphoneos", "arm64e"),
                      ADD_DEVICE("J310AP", "iPad mini 6",                "iphoneos", "arm64e"),
                      ADD_DEVICE("J311AP", "iPad mini 6 (Cellular)",     "iphoneos", "arm64e"),

                      // iPhone

                      ADD_DEVICE("M68AP",  "iPhone",                     "iphoneos", "armv7"),
                      ADD_DEVICE("N82AP",  "iPhone 3G",                  "iphoneos", "armv7"),
                      ADD_DEVICE("N88AP",  "iPhone 3GS",                 "iphoneos", "armv7"),
                      ADD_DEVICE("N90AP",  "iPhone 4 (GSM)",             "iphoneos", "armv7"),
                      ADD_DEVICE("N92AP",  "iPhone 4 (CDMA)",            "iphoneos", "armv7"),
                      ADD_DEVICE("N90BAP", "iPhone 4 (GSM, revision A)", "iphoneos", "armv7"),
                      ADD_DEVICE("N94AP",  "iPhone 4S",                  "iphoneos", "armv7"),
                      ADD_DEVICE("N41AP",  "iPhone 5 (GSM)",             "iphoneos", "armv7s"),
                      ADD_DEVICE("N42AP",  "iPhone 5 (Global/CDMA)",     "iphoneos", "armv7s"),
                      ADD_DEVICE("N48AP",  "iPhone 5c (GSM)",            "iphoneos", "armv7s"),
                      ADD_DEVICE("N49AP",  "iPhone 5c (Global/CDMA)",    "iphoneos", "armv7s"),
                      ADD_DEVICE("N51AP",  "iPhone 5s (GSM)",            "iphoneos", "arm64"),
                      ADD_DEVICE("N53AP",  "iPhone 5s (Global/CDMA)",    "iphoneos", "arm64"),
                      ADD_DEVICE("N61AP",  "iPhone 6 (GSM)",             "iphoneos", "arm64"),
                      ADD_DEVICE("N56AP",  "iPhone 6 Plus",              "iphoneos", "arm64"),
                      ADD_DEVICE("N71mAP", "iPhone 6s",                  "iphoneos", "arm64"),
                      ADD_DEVICE("N71AP",  "iPhone 6s",                  "iphoneos", "arm64"),
                      ADD_DEVICE("N66AP",  "iPhone 6s Plus",             "iphoneos", "arm64"),
                      ADD_DEVICE("N66mAP", "iPhone 6s Plus",             "iphoneos", "arm64"),
                      ADD_DEVICE("N69AP",  "iPhone SE",                  "iphoneos", "arm64"),
                      ADD_DEVICE("N69uAP", "iPhone SE",                  "iphoneos", "arm64"),
                      ADD_DEVICE("D10AP",  "iPhone 7",                   "iphoneos", "arm64"),
                      ADD_DEVICE("D101AP", "iPhone 7",                   "iphoneos", "arm64"),
                      ADD_DEVICE("D11AP",  "iPhone 7 Plus",              "iphoneos", "arm64"),
                      ADD_DEVICE("D111AP", "iPhone 7 Plus",              "iphoneos", "arm64"),
                      ADD_DEVICE("D20AP",  "iPhone 8",                   "iphoneos", "arm64"),
                      ADD_DEVICE("D20AAP", "iPhone 8",                   "iphoneos", "arm64"),
                      ADD_DEVICE("D201AP", "iPhone 8",                   "iphoneos", "arm64"),
                      ADD_DEVICE("D201AAP","iPhone 8",                   "iphoneos", "arm64"),
                      ADD_DEVICE("D21AP",  "iPhone 8 Plus",              "iphoneos", "arm64"),
                      ADD_DEVICE("D21AAP", "iPhone 8 Plus",              "iphoneos", "arm64"),
                      ADD_DEVICE("D211AP", "iPhone 8 Plus",              "iphoneos", "arm64"),
                      ADD_DEVICE("D211AAP","iPhone 8 Plus",              "iphoneos", "arm64"),
                      ADD_DEVICE("D22AP",  "iPhone X",                   "iphoneos", "arm64"),
                      ADD_DEVICE("D221AP", "iPhone X",                   "iphoneos", "arm64"),
                      ADD_DEVICE("N841AP", "iPhone XR",                  "iphoneos", "arm64e"),
                      ADD_DEVICE("D321AP", "iPhone XS",                  "iphoneos", "arm64e"),
                      ADD_DEVICE("D331pAP","iPhone XS Max",              "iphoneos", "arm64e"),
                      ADD_DEVICE("N104AP", "iPhone 11",                  "iphoneos", "arm64e"),
                      ADD_DEVICE("D421AP", "iPhone 11 Pro",              "iphoneos", "arm64e"),
                      ADD_DEVICE("D431AP", "iPhone 11 Pro Max",          "iphoneos", "arm64e"),
                      ADD_DEVICE("D79AP",  "iPhone SE 2G",               "iphoneos", "arm64e"),
                      ADD_DEVICE("D52gAP", "iPhone 12 Mini",             "iphoneos", "arm64e"),
                      ADD_DEVICE("D53gAP", "iPhone 12",                  "iphoneos", "arm64e"),
                      ADD_DEVICE("D53pAP", "iPhone 12 Pro",              "iphoneos", "arm64e"),
                      ADD_DEVICE("D54pAP", "iPhone 12 Pro Max",          "iphoneos", "arm64e"),
                      ADD_DEVICE("D16AP",  "iPhone 13 Mini",             "iphoneos", "arm64e"),
                      ADD_DEVICE("D17AP",  "iPhone 13",                  "iphoneos", "arm64e"),
                      ADD_DEVICE("D63AP",  "iPhone 13 Pro",              "iphoneos", "arm64e"),
                      ADD_DEVICE("D64AP",  "iPhone 13 Pro Max",          "iphoneos", "arm64e"),
                      ADD_DEVICE("D49AP",  "iPhone SE 3G",               "iphoneos", "arm64e"),
                      ADD_DEVICE("D27AP",  "iPhone 14",                  "iphoneos", "arm64e"),
                      ADD_DEVICE("D28AP",  "iPhone 14 Plus",             "iphoneos", "arm64e"),
                      ADD_DEVICE("D73AP",  "iPhone 14 Pro",              "iphoneos", "arm64e"),
                      ADD_DEVICE("D74AP",  "iPhone 14 Pro Max",          "iphoneos", "arm64e"),
                      ADD_DEVICE("D37AP",  "iPhone 15",                  "iphoneos", "arm64e"),
                      ADD_DEVICE("D38AP",  "iPhone 15 Plus",             "iphoneos", "arm64e"),
                      ADD_DEVICE("D83AP",  "iPhone 15 Pro",              "iphoneos", "arm64e"),
                      ADD_DEVICE("D84AP",  "iPhone 15 Pro Max",          "iphoneos", "arm64e"),

                      // Apple TV

                      ADD_DEVICE("K66AP",  "Apple TV 2G",                "appletvos", "armv7"),
                      ADD_DEVICE("J33AP",  "Apple TV 3G",                "appletvos", "armv7"),
                      ADD_DEVICE("J33IAP", "Apple TV 3.1G",              "appletvos", "armv7"),
                      ADD_DEVICE("J42dAP", "Apple TV 4G",                "appletvos", "arm64"),
                      ADD_DEVICE("J105aAP","Apple TV 4K",                "appletvos", "arm64"),
                      ADD_DEVICE("J305AP", "Apple TV 4K 2",              "appletvos", "arm64e"),

                      // Apple Watch
                      ADD_DEVICE("N121sAP","Apple Watch Series 3 (GPS)", "watchos", "armv7k"),
                      ADD_DEVICE("N157bAP","Apple Watch Series 6",       "watchos", "arm64"),
                      };
Yonita34567 commented 2 months ago

typedef struct errorcode_to_id { unsigned int error; const char* id; } errorcode_to_id_t;

typedef struct error_id_to_message { const char id; const char message; } error_id_to_message_t;

// Parts of error code to localization id map is taken from SDMMobileDevice framework. Associated license is bellow. // https://github.com/samdmarshall/SDMMobileDevice/blob/master/Framework/MobileDevice/Error/SDMMD_Error.h // // Copyright (c) 2014, Sam Marshall // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the distribution. // // 3. Neither the name of Sam Marshall nor the names of its contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static errorcode_to_id_t errorcode_to_id[] = { { 0x00000000, "kAMDSuccess" }, { 0xe8000001, "kAMDUndefinedError" }, { 0xe8000002, "kAMDBadHeaderError" }, { 0xe8000003, "kAMDNoResourcesError" }, { 0xe8000004, "kAMDReadError" }, { 0xe8000005, "kAMDWriteError" }, { 0xe8000006, "kAMDUnknownPacketError" }, { 0xe8000007, "kAMDInvalidArgumentError" }, { 0xe8000008, "kAMDNotFoundError" }, { 0xe8000009, "kAMDIsDirectoryError" }, { 0xe800000a, "kAMDPermissionError" }, { 0xe800000b, "kAMDNotConnectedError" }, { 0xe800000c, "kAMDTimeOutError" }, { 0xe800000d, "kAMDOverrunError" }, { 0xe800000e, "kAMDEOFError" }, { 0xe800000f, "kAMDUnsupportedError" }, { 0xe8000010, "kAMDFileExistsError" }, { 0xe8000011, "kAMDBusyError" }, { 0xe8000012, "kAMDCryptoError" }, { 0xe8000013, "kAMDInvalidResponseError" }, { 0xe8000014, "kAMDMissingKeyError" }, { 0xe8000015, "kAMDMissingValueError" }, { 0xe8000016, "kAMDGetProhibitedError" }, { 0xe8000017, "kAMDSetProhibitedError" }, { 0xe8000018, "kAMDRemoveProhibitedError" }, { 0xe8000019, "kAMDImmutableValueError" }, { 0xe800001a, "kAMDPasswordProtectedError" }, { 0xe800001b, "kAMDMissingHostIDError" }, { 0xe800001c, "kAMDInvalidHostIDError" }, { 0xe800001d, "kAMDSessionActiveError" }, { 0xe800001e, "kAMDSessionInactiveError" }, { 0xe800001f, "kAMDMissingSessionIDError" }, { 0xe8000020, "kAMDInvalidSessionIDError" }, { 0xe8000021, "kAMDMissingServiceError" }, { 0xe8000022, "kAMDInvalidServiceError" }, { 0xe8000023, "kAMDInvalidCheckinError" }, { 0xe8000024, "kAMDCheckinTimeoutError" }, { 0xe8000025, "kAMDMissingPairRecordError" }, { 0xe8000026, "kAMDInvalidActivationRecordError" }, { 0xe8000027, "kAMDMissingActivationRecordError" }, { 0xe8000028, "kAMDWrongDroidError" }, { 0xe8000029, "kAMDSUVerificationError" }, { 0xe800002a, "kAMDSUPatchError" }, { 0xe800002b, "kAMDSUFirmwareError" }, { 0xe800002c, "kAMDProvisioningProfileNotValid" }, { 0xe800002d, "kAMDSendMessageError" }, { 0xe800002e, "kAMDReceiveMessageError" }, { 0xe800002f, "kAMDMissingOptionsError" }, { 0xe8000030, "kAMDMissingImageTypeError" }, { 0xe8000031, "kAMDDigestFailedError" }, { 0xe8000032, "kAMDStartServiceError" }, { 0xe8000033, "kAMDInvalidDiskImageError" }, { 0xe8000034, "kAMDMissingDigestError" }, { 0xe8000035, "kAMDMuxError" }, { 0xe8000036, "kAMDApplicationAlreadyInstalledError" }, { 0xe8000037, "kAMDApplicationMoveFailedError" }, { 0xe8000038, "kAMDApplicationSINFCaptureFailedError" }, { 0xe8000039, "kAMDApplicationSandboxFailedError" }, { 0xe800003a, "kAMDApplicationVerificationFailedError" }, { 0xe800003b, "kAMDArchiveDestructionFailedError" }, { 0xe800003c, "kAMDBundleVerificationFailedError" }, { 0xe800003d, "kAMDCarrierBundleCopyFailedError" }, { 0xe800003e, "kAMDCarrierBundleDirectoryCreationFailedError" }, { 0xe800003f, "kAMDCarrierBundleMissingSupportedSIMsError" }, { 0xe8000040, "kAMDCommCenterNotificationFailedError" }, { 0xe8000041, "kAMDContainerCreationFailedError" }, { 0xe8000042, "kAMDContainerP0wnFailedError" }, { 0xe8000043, "kAMDContainerRemovalFailedError" }, { 0xe8000044, "kAMDEmbeddedProfileInstallFailedError" }, { 0xe8000045, "kAMDErrorError" }, { 0xe8000046, "kAMDExecutableTwiddleFailedError" }, { 0xe8000047, "kAMDExistenceCheckFailedError" }, { 0xe8000048, "kAMDInstallMapUpdateFailedError" }, { 0xe8000049, "kAMDManifestCaptureFailedError" }, { 0xe800004a, "kAMDMapGenerationFailedError" }, { 0xe800004b, "kAMDMissingBundleExecutableError" }, { 0xe800004c, "kAMDMissingBundleIdentifierError" }, { 0xe800004d, "kAMDMissingBundlePathError" }, { 0xe800004e, "kAMDMissingContainerError" }, { 0xe800004f, "kAMDNotificationFailedError" }, { 0xe8000050, "kAMDPackageExtractionFailedError" }, { 0xe8000051, "kAMDPackageInspectionFailedError" }, { 0xe8000052, "kAMDPackageMoveFailedError" }, { 0xe8000053, "kAMDPathConversionFailedError" }, { 0xe8000054, "kAMDRestoreContainerFailedError" }, { 0xe8000055, "kAMDSeatbeltProfileRemovalFailedError" }, { 0xe8000056, "kAMDStageCreationFailedError" }, { 0xe8000057, "kAMDSymlinkFailedError" }, { 0xe8000058, "kAMDiTunesArtworkCaptureFailedError" }, { 0xe8000059, "kAMDiTunesMetadataCaptureFailedError" }, { 0xe800005a, "kAMDAlreadyArchivedError" }, { 0xe800005b, "kAMDServiceLimitError" }, { 0xe800005c, "kAMDInvalidPairRecordError" }, { 0xe800005d, "kAMDServiceProhibitedError" }, { 0xe800005e, "kAMDCheckinSetupFailedError" }, { 0xe800005f, "kAMDCheckinConnectionFailedError" }, { 0xe8000060, "kAMDCheckinReceiveFailedError" }, { 0xe8000061, "kAMDCheckinResponseFailedError" }, { 0xe8000062, "kAMDCheckinSendFailedError" }, { 0xe8000063, "kAMDMuxCreateListenerError" }, { 0xe8000064, "kAMDMuxGetListenerError" }, { 0xe8000065, "kAMDMuxConnectError" }, { 0xe8000066, "kAMDUnknownCommandError" }, { 0xe8000067, "kAMDAPIInternalError" }, { 0xe8000068, "kAMDSavePairRecordFailedError" }, { 0xe8000069, "kAMDCheckinOutOfMemoryError" }, { 0xe800006a, "kAMDDeviceTooNewError" }, { 0xe800006b, "kAMDDeviceRefNoGood" }, { 0xe800006c, "kAMDCannotTranslateError" }, { 0xe800006d, "kAMDMobileImageMounterMissingImageSignature" }, { 0xe800006e, "kAMDMobileImageMounterResponseCreationFailed" }, { 0xe800006f, "kAMDMobileImageMounterMissingImageType" }, { 0xe8000070, "kAMDMobileImageMounterMissingImagePath" }, { 0xe8000071, "kAMDMobileImageMounterImageMapLoadFailed" }, { 0xe8000072, "kAMDMobileImageMounterAlreadyMounted" }, { 0xe8000073, "kAMDMobileImageMounterImageMoveFailed" }, { 0xe8000074, "kAMDMobileImageMounterMountPathMissing" }, { 0xe8000075, "kAMDMobileImageMounterMountPathNotEmpty" }, { 0xe8000076, "kAMDMobileImageMounterImageMountFailed" }, { 0xe8000077, "kAMDMobileImageMounterTrustCacheLoadFailed" }, { 0xe8000078, "kAMDMobileImageMounterDigestFailed" }, { 0xe8000079, "kAMDMobileImageMounterDigestCreationFailed" }, { 0xe800007a, "kAMDMobileImageMounterImageVerificationFailed" }, { 0xe800007b, "kAMDMobileImageMounterImageInfoCreationFailed" }, { 0xe800007c, "kAMDMobileImageMounterImageMapStoreFailed" }, { 0xe800007d, "kAMDBonjourSetupError" }, { 0xe800007e, "kAMDDeviceOSVersionTooLow" }, { 0xe800007f, "kAMDNoWifiSyncSupportError" }, { 0xe8000080, "kAMDDeviceFamilyNotSupported" }, { 0xe8000081, "kAMDEscrowLockedError" }, { 0xe8000082, "kAMDPairingProhibitedError" }, { 0xe8000083, "kAMDProhibitedBySupervision" }, { 0xe8000084, "kAMDDeviceDisconnectedError" }, { 0xe8000085, "kAMDTooBigError" }, { 0xe8000086, "kAMDPackagePatchFailedError" }, { 0xe8000087, "kAMDIncorrectArchitectureError" }, { 0xe8000088, "kAMDPluginCopyFailedError" }, { 0xe8000089, "kAMDBreadcrumbFailedError" }, { 0xe800008a, "kAMDBreadcrumbUnlockError" }, { 0xe800008b, "kAMDGeoJSONCaptureFailedError" }, { 0xe800008c, "kAMDNewsstandArtworkCaptureFailedError" }, { 0xe800008d, "kAMDMissingCommandError" }, { 0xe800008e, "kAMDNotEntitledError" }, { 0xe800008f, "kAMDMissingPackagePathError" }, { 0xe8000090, "kAMDMissingContainerPathError" }, { 0xe8000091, "kAMDMissingApplicationIdentifierError" }, { 0xe8000092, "kAMDMissingAttributeValueError" }, { 0xe8000093, "kAMDLookupFailedError" }, { 0xe8000094, "kAMDDictCreationFailedError" }, { 0xe8000095, "kAMDUserDeniedPairingError" }, { 0xe8000096, "kAMDPairingDialogResponsePendingError" }, { 0xe8000097, "kAMDInstallProhibitedError" }, { 0xe8000098, "kAMDUninstallProhibitedError" }, { 0xe8000099, "kAMDFMiPProtectedError" }, { 0xe800009a, "kAMDMCProtected" }, { 0xe800009b, "kAMDMCChallengeRequired" }, { 0xe800009c, "kAMDMissingBundleVersionError" }, { 0xe800009d, "kAMDAppBlacklistedError" }, { 0xe800009e, "This app contains an app extension with an illegal bundle identifier. App extension bundle identifiers must have a prefix consisting of their containing application's bundle identifier followed by a '.'." }, { 0xe800009f, "If an app extension defines the XPCService key in its Info.plist, it must have a dictionary value." }, { 0xe80000a0, "App extensions must define the NSExtension key with a dictionary value in their Info.plist." }, { 0xe80000a1, "If an app extension defines the CFBundlePackageType key in its Info.plist, it must have the value \"XPC!\"." }, { 0xe80000a2, "App extensions must define either NSExtensionMainStoryboard or NSExtensionPrincipalClass keys in the NSExtension dictionary in their Info.plist." }, { 0xe80000a3, "If an app extension defines the NSExtensionContextClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." }, { 0xe80000a4, "If an app extension defines the NSExtensionContextHostClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." }, { 0xe80000a5, "If an app extension defines the NSExtensionViewControllerHostClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." }, { 0xe80000a6, "This app contains an app extension that does not define the NSExtensionPointIdentifier key in its Info.plist. This key must have a reverse-DNS format string value." }, { 0xe80000a7, "This app contains an app extension that does not define the NSExtensionPointIdentifier key in its Info.plist with a valid reverse-DNS format string value." }, { 0xe80000a8, "If an app extension defines the NSExtensionAttributes key in the NSExtension dictionary in its Info.plist, it must have a dictionary value." }, { 0xe80000a9, "If an app extension defines the NSExtensionPointName key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." }, { 0xe80000aa, "If an app extension defines the NSExtensionPointVersion key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." }, { 0xe80000ab, "This app or a bundle it contains does not define the CFBundleName key in its Info.plist with a string value containing one or more characters." }, { 0xe80000ac, "This app or a bundle it contains does not define the CFBundleDisplayName key in its Info.plist with a string value containing one or more characters." }, { 0xe80000ad, "This app or a bundle it contains defines the CFBundleShortVersionStringKey key in its Info.plist with a non-string value or a zero-length string value." }, { 0xe80000ae, "This app or a bundle it contains defines the RunLoopType key in the XPCService dictionary in its Info.plist with a non-string value or a zero-length string value." }, { 0xe80000af, "This app or a bundle it contains defines the ServiceType key in the XPCService dictionary in its Info.plist with a non-string value or a zero-length string value." }, { 0xe80000b0, "This application or a bundle it contains has the same bundle identifier as this application or another bundle that it contains. Bundle identifiers must be unique." }, { 0xe80000b1, "This app contains an app extension that specifies an extension point identifier that is not supported on this version of iOS for the value of the NSExtensionPointIdentifier key in its Info.plist." }, { 0xe80000b2, "This app contains multiple app extensions that are file providers. Apps are only allowed to contain at most a single file provider app extension." }, { 0xe80000b3, "kMobileHouseArrestMissingCommand" }, { 0xe80000b4, "kMobileHouseArrestUnknownCommand" }, { 0xe80000b5, "kMobileHouseArrestMissingIdentifier" }, { 0xe80000b6, "kMobileHouseArrestDictionaryFailed" }, { 0xe80000b7, "kMobileHouseArrestInstallationLookupFailed" }, { 0xe80000b8, "kMobileHouseArrestApplicationLookupFailed" }, { 0xe80000b9, "kMobileHouseArrestMissingContainer" }, // 0xe80000ba does not exist { 0xe80000bb, "kMobileHouseArrestPathConversionFailed" }, { 0xe80000bc, "kMobileHouseArrestPathMissing" }, { 0xe80000bd, "kMobileHouseArrestInvalidPath" }, { 0xe80000be, "kAMDMismatchedApplicationIdentifierEntitlementError" }, { 0xe80000bf, "kAMDInvalidSymlinkError" }, { 0xe80000c0, "kAMDNoSpaceError" }, { 0xe80000c1, "The WatchKit app extension must have, in its Info.plist's NSExtension dictionary's NSExtensionAttributes dictionary, the key WKAppBundleIdentifier with a value equal to the associated WatchKit app's bundle identifier." }, { 0xe80000c2, "This app is not a valid AppleTV Stub App" }, { 0xe80000c3, "kAMDBundleiTunesMetadataVersionMismatchError" }, { 0xe80000c4, "kAMDInvalidiTunesMetadataPlistError" }, { 0xe80000c5, "kAMDMismatchedBundleIDSigningIdentifierError" }, { 0xe80000c6, "This app contains multiple WatchKit app extensions. Only a single WatchKit extension is allowed." }, { 0xe80000c7, "A WatchKit app within this app is not a valid bundle." }, { 0xe80000c8, "kAMDDeviceNotSupportedByThinningError" }, { 0xe80000c9, "The UISupportedDevices key in this app's Info.plist does not specify a valid set of supported devices." }, { 0xe80000ca, "This app contains an app extension with an illegal bundle identifier. App extension bundle identifiers must have a prefix consisting of their containing application's bundle identifier followed by a '.', with no further '.' characters after the prefix." }, { 0xe80000cb, "kAMDAppexBundleIDConflictWithOtherIdentifierError" }, { 0xe80000cc, "kAMDBundleIDConflictWithOtherIdentifierError" }, { 0xe80000cd, "This app contains multiple WatchKit 1.0 apps. Only a single WatchKit 1.0 app is allowed." }, { 0xe80000ce, "This app contains multiple WatchKit 2.0 apps. Only a single WatchKit 2.0 app is allowed." }, { 0xe80000cf, "The WatchKit app has an invalid stub executable." }, { 0xe80000d0, "The WatchKit app has multiple app extensions. Only a single WatchKit extension is allowed in a WatchKit app, and only if this is a WatchKit 2.0 app." }, { 0xe80000d1, "The WatchKit 2.0 app contains non-WatchKit app extensions. Only WatchKit app extensions are allowed in WatchKit apps." }, { 0xe80000d2, "The WatchKit app has one or more embedded frameworks. Frameworks are only allowed in WatchKit app extensions in WatchKit 2.0 apps." }, { 0xe80000d3, "This app contains a WatchKit 1.0 app with app extensions. This is not allowed." }, { 0xe80000d4, "This app contains a WatchKit 2.0 app without an app extension. WatchKit 2.0 apps must contain a WatchKit app extension." }, { 0xe80000d5, "The WatchKit app's Info.plist must have a WKCompanionAppBundleIdentifier key set to the bundle identifier of the companion app." }, { 0xe80000d6, "The WatchKit app's Info.plist contains a non-string key." }, { 0xe80000d7, "The WatchKit app's Info.plist contains a key that is not in the whitelist of allowed keys for a WatchKit app." }, { 0xe80000d8, "The WatchKit 1.0 and a WatchKit 2.0 apps within this app must have have the same bundle identifier." }, { 0xe80000d9, "This app contains a WatchKit app with an invalid bundle identifier. The bundle identifier of a WatchKit app must have a prefix consisting of the companion app's bundle identifier, followed by a '.'." }, { 0xe80000da, "This app contains a WatchKit app where the UIDeviceFamily key in its Info.plist does not specify the value 4 to indicate that it's compatible with the Apple Watch device type." }, { 0xe80000db, "The device is out of storage for apps. Please remove some apps from the device and try again." }, { 0xe80000dc, "This app or an app that it contains has a Siri Intents app extension that is missing the IntentsSupported array in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist." }, { 0xe80000dd, "This app or an app that it contains has a Siri Intents app extension that does not correctly define the IntentsRestrictedWhileLocked key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist. The key's value must be an array of strings." }, { 0xe80000de, "This app or an app that it contains has a Siri Intents app extension that declares values in its IntentsRestrictedWhileLocked key's array value that are not in its IntentsSupported key's array value (in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist)." }, { 0xe80000df, "This app or an app that it contains declares multiple Siri Intents app extensions that declare one or more of the same values in the IntentsSupported array in the NSExtensionAttributes dictionary in the NSExtension dictionary in their Info.plist. IntentsSupported must be distinct among a given Siri Intents extension type within an app." }, { 0xe80000e0, "The WatchKit 2.0 app, which expects to be compatible with watchOS versions earlier than 3.0, contains a non-WatchKit extension in a location that's not compatible with watchOS versions earlier than 3.0." }, { 0xe80000e1, "The WatchKit 2.0 app, which expects to be compatible with watchOS versions earlier than 3.0, contains a framework in a location that's not compatible with watchOS versions earlier than 3.0." }, { 0xe80000e2, "kAMDMobileImageMounterDeviceLocked" }, { 0xe80000e3, "kAMDInvalidSINFError" }, { 0xe80000e4, "Multiple iMessage app extensions were found in this app. Only one is allowed." }, { 0xe80000e5, "This iMessage application is missing its required iMessage app extension." }, { 0xe80000e6, "This iMessage application contains an app extension type other than an iMessage app extension. iMessage applications may only contain one iMessage app extension and may not contain other types of app extensions." }, { 0xe80000e7, "This app contains a WatchKit app with one or more Siri Intents app extensions that declare IntentsSupported that are not declared in any of the companion app's Siri Intents app extensions. WatchKit Siri Intents extensions' IntentsSupported values must be a subset of the companion app's Siri Intents extensions' IntentsSupported values." }, { 0xe80000e8, "kAMDRequireCUPairingCodeError" }, { 0xe80000e9, "kAMDRequireCUPairingBackoffError" }, { 0xe80000ea, "kAMDCUPairingError" }, { 0xe80000eb, "kAMDCUPairingContinueError" }, { 0xe80000ec, "kAMDCUPairingResetError" }, { 0xe80000ed, "kAMDRequireCUPairingError" }, { 0xe80000ee, "kAMDPasswordRequiredError" },

// Errors without id->string mapping.
{ 0xe8008001, "An unknown error has occurred." },
{ 0xe8008002, "Attempted to modify an immutable provisioning profile." },
{ 0xe8008003, "This provisioning profile is malformed." },
{ 0xe8008004, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
{ 0xe8008005, "This provisioning profile is malformed." },
{ 0xe8008006, "This provisioning profile is malformed." },
{ 0xe8008007, "This provisioning profile is malformed." },
{ 0xe8008008, "This provisioning profile is malformed." },
{ 0xe8008009, "The signature was not valid." },
{ 0xe800800a, "Unable to allocate memory." },
{ 0xe800800b, "A file operation failed." },
{ 0xe800800c, "There was an error communicating with your device." },
{ 0xe800800d, "There was an error communicating with your device." },
{ 0xe800800e, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
{ 0xe800800f, "The application's signature is valid but it does not match the expected hash." },
{ 0xe8008010, "This provisioning profile is unsupported." },
{ 0xe8008011, "This provisioning profile has expired." },
{ 0xe8008012, "This provisioning profile cannot be installed on this device." },
{ 0xe8008013, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
{ 0xe8008014, "The executable contains an invalid signature." },
{ 0xe8008015, "A valid provisioning profile for this executable was not found." },
{ 0xe8008016, "The executable was signed with invalid entitlements." },
{ 0xe8008017, "A signed resource has been added, modified, or deleted." },
{ 0xe8008018, "The identity used to sign the executable is no longer valid." },
{ 0xe8008019, "The application does not have a valid signature." },
{ 0xe800801a, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
{ 0xe800801b, "There was an error communicating with your device." },
{ 0xe800801c, "No code signature found." },
{ 0xe800801d, "Rejected by policy." },
{ 0xe800801e, "The requested profile does not exist (it may have been removed)." },
{ 0xe800801f, "Attempted to install a Beta profile without the proper entitlement." },
{ 0xe8008020, "Attempted to install a Beta profile over lockdown connection." },
{ 0xe8008021, "The maximum number of apps for free development profiles has been reached." },
{ 0xe8008022, "An error occured while accessing the profile database." },
{ 0xe8008023, "An error occured while communicating with the agent." },
{ 0xe8008024, "The provisioning profile is banned." },
{ 0xe8008025, "The user did not explicitly trust the provisioning profile." },
{ 0xe8008026, "The provisioning profile requires online authorization." },
{ 0xe8008027, "The cdhash is not in the trust cache." },
{ 0xe8008028, "Invalid arguments or option combination." },

};

const int errorcode_to_id_count = sizeof(errorcode_to_id) / sizeof(errorcode_to_id_t);

// Taken from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/English.lproj/Localizable.strings error_id_to_message_t error_id_to_message[] = { { "kAMDAPIInternalError", "There was an internal API error." }, { "kAMDAlreadyArchivedError", "The application is already archived." }, { "kAMDAppBlacklistedError", "This app is not allowed to be installed on this device." }, { "kAMDAppexBundleIDConflictWithOtherIdentifierError", "This application contains an app extension with a bundle identifier that conflicts with the bundle identifier of another app or app extension already installed." }, { "kAMDApplicationAlreadyInstalledError", "A system application with the given bundle identifier is already installed on the device and cannot be replaced." }, { "kAMDApplicationMoveFailedError", "The application could not be moved into place on the device." }, { "kAMDApplicationSandboxFailedError", "The application could not be sandboxed." }, { "kAMDApplicationVerificationFailedError", "The application could not be verified." }, { "kAMDArchiveDestructionFailedError", "Could not remove the application archive." }, { "kAMDBadHeaderError", "Could not transfer file." }, { "kAMDBreadcrumbFailedError", "Could not write installation breadcrumb." }, { "kAMDBreadcrumbUnlockError", "Could not update installation breadcrumb." }, { "kAMDBundleIDConflictWithOtherIdentifierError", "This application's bundle identifier conflicts with the identifier of another app or app extension already installed." }, { "kAMDBundleVerificationFailedError", "The carrier bundle could not be verified." }, { "kAMDBundleiTunesMetadataVersionMismatchError", "This application's iTunesMetadata.plist specifies versions that do not match the versions listed for the app in its Info.plist" }, { "kAMDBusyError", "The device is busy." }, { "kAMDCUPairingContinueError", "Continue pairing process over the network." }, { "kAMDCUPairingError", "General failure while pairing over the network." }, { "kAMDCUPairingResetError", "Pairing was reset due to earlier issues, try again." }, { "kAMDCannotTranslateError", "Could not translate messages from device" }, { "kAMDCarrierBundleCopyFailedError", "Could not install the carrier bundle." }, { "kAMDCarrierBundleDirectoryCreationFailedError", "Could not create the carrier bundle directory." }, { "kAMDCarrierBundleMissingSupportedSIMsError", "There are no supported SIMs for this carrier bundle." }, { "kAMDCheckinConnectionFailedError", "The service did not start properly on the device." }, { "kAMDCheckinOutOfMemoryError", "The service did not start properly on the device." }, { "kAMDCheckinReceiveFailedError", "The service did not start properly on the device." }, { "kAMDCheckinResponseFailedError", "The service did not start properly on the device." }, { "kAMDCheckinSendFailedError", "The service did not start properly on the device." }, { "kAMDCheckinSetupFailedError", "Could not start service on device" }, { "kAMDCheckinTimeoutError", "The service did not start properly on the device." }, { "kAMDCommCenterNotificationFailedError", "Could not listen for notification from the baseband." }, { "kAMDContainerCreationFailedError", "Could not create application container." }, { "kAMDContainerP0wnFailedError", "Could not repair permissions on application container." }, { "kAMDContainerRemovalFailedError", "Could not remove the application container." }, { "kAMDCryptoError", "Could not establish a secure connection to the device." }, { "kAMDDeviceDisconnectedError", "This device is no longer connected." }, { "kAMDDeviceFamilyNotSupported", "This application does not support this kind of device." }, { "kAMDDeviceNotSupportedByThinningError", "This application is not built for this device." }, { "kAMDDeviceOSVersionTooLow", "The device OS version is too low." }, { "kAMDDeviceRefNoGood", "This device is no longer connected." }, { "kAMDDeviceTooNewError", "This application needs to be updated." }, { "kAMDDictCreationFailedError", "Could not extract capabilities from the request." }, { "kAMDDigestFailedError", "Could not read disk image." }, { "kAMDEOFError", "End of file." }, { "kAMDEmbeddedProfileInstallFailedError", "Could not install the embedded provisioning profile." }, { "kAMDErrorError", "An error occurred." }, { "kAMDEscrowLockedError", "Device is not available until first unlock after boot." }, { "kAMDExecutableTwiddleFailedError", "Could not change executable permissions on the application." }, { "kAMDExistenceCheckFailedError", "Could not check to see if the application already exists." }, { "kAMDFMiPProtectedError", "The device is in lost mode." }, { "kAMDFileExistsError", "The file already exists." }, { "kAMDGeoJSONCaptureFailedError", "Could not save the GeoJSON data." }, { "kAMDGetProhibitedError", "Cannot retrieve value from the passcode locked device." }, { "kAMDImmutableValueError", "This value cannot be changed." }, { "kAMDIncorrectArchitectureError", "This application does not support this device's CPU type." }, { "kAMDInstallMapUpdateFailedError", "Could not update the installed applications list." }, { "kAMDInstallProhibitedError", "Installation of apps is prohibited by a policy on the device." }, { "kAMDInvalidActivationRecordError", "The activation record is not valid." }, { "kAMDInvalidArgumentError", "The argument is invalid." }, { "kAMDInvalidCheckinError", "Could not start service on device" }, { "kAMDInvalidDiskImageError", "The disk image is invalid." }, { "kAMDInvalidHostIDError", "The device does not recognize this host." }, { "kAMDInvalidPairRecordError", "The host is no longer paired with the device." }, { "kAMDInvalidResponseError", "Received an unexpected response from the device." }, { "kAMDInvalidSINFError", "The encryption information included with this application is not valid so this application cannot be installed on this device." }, { "kAMDInvalidServiceError", "The service is invalid." }, { "kAMDInvalidSessionIDError", "The session ID is invalid." }, { "kAMDInvalidSymlinkError", "The bundle contained an invalid symlink." }, { "kAMDInvalidiTunesMetadataPlistError", "This application's iTunesMetadata.plist is not valid." }, { "kAMDIsDirectoryError", "The path is a directory." }, { "kAMDLookupFailedError", "Could not list installed applications." }, { "kAMDMCChallengeRequired", "A policy on the device requires secure pairing." }, { "kAMDMCProtected", "Pairing is prohibited by a policy on the device." }, { "kAMDManifestCaptureFailedError", "Could not save the application manifest." }, { "kAMDMapGenerationFailedError", "Could not generate the map." }, { "kAMDMismatchedApplicationIdentifierEntitlementError", "This application's application-identifier entitlement does not match that of the installed application. These values must match for an upgrade to be allowed." }, { "kAMDMismatchedBundleIDSigningIdentifierError", "This application's bundle identifier does not match its code signing identifier." }, { "kAMDMissingActivationRecordError", "The activation record could not be found." }, { "kAMDMissingApplicationIdentifierError", "Request was missing the application identifier." }, { "kAMDMissingAttributeValueError", "Request was missing a required value." }, { "kAMDMissingBundleExecutableError", "The application bundle does not contain an executable." }, { "kAMDMissingBundleIdentifierError", "The application bundle does not contain a valid identifier." }, { "kAMDMissingBundlePathError", "Could not determine the application bundle path." }, { "kAMDMissingBundleVersionError", "The bundle's Info.plist does not contain a CFBundleVersion key or its value is not a string." }, { "kAMDMissingCommandError", "The request did not contain a command." }, { "kAMDMissingContainerError", "Could not find the container for the installed application." }, { "kAMDMissingContainerPathError", "Request was missing the container path." }, { "kAMDMissingDigestError", "The digest is missing." }, { "kAMDMissingHostIDError", "The device does not recognize this host." }, { "kAMDMissingImageTypeError", "The image is missing." }, { "kAMDMissingKeyError", "The key is missing." }, { "kAMDMissingOptionsError", "The options are missing." }, { "kAMDMissingPackagePathError", "Request was missing the package path." }, { "kAMDMissingPairRecordError", "The host is not paired with the device." }, { "kAMDMissingServiceError", "The service is missing." }, { "kAMDMissingSessionIDError", "The session ID is missing." }, { "kAMDMissingValueError", "The value is missing." }, { "kAMDMobileImageMounterAlreadyMounted", "Image is already mounted." }, { "kAMDMobileImageMounterDeviceLocked", "The device is locked." }, { "kAMDMobileImageMounterDigestCreationFailed", "Could not support development." }, { "kAMDMobileImageMounterDigestFailed", "Could not support development." }, { "kAMDMobileImageMounterImageInfoCreationFailed", "Could not support development." }, { "kAMDMobileImageMounterImageMapLoadFailed", "Could not support development." }, { "kAMDMobileImageMounterImageMapStoreFailed", "Could not support development." }, { "kAMDMobileImageMounterImageMountFailed", "Could not support development." }, { "kAMDMobileImageMounterImageMoveFailed", "Could not support development." }, { "kAMDMobileImageMounterImageVerificationFailed", "Could not support development." }, { "kAMDMobileImageMounterMissingImagePath", "Could not support development." }, { "kAMDMobileImageMounterMissingImageSignature", "Could not support development." }, { "kAMDMobileImageMounterMissingImageType", "Could not support development." }, { "kAMDMobileImageMounterMountPathMissing", "Could not support development." }, { "kAMDMobileImageMounterMountPathNotEmpty", "Could not support development." }, { "kAMDMobileImageMounterResponseCreationFailed", "Could not support development." }, { "kAMDMobileImageMounterTrustCacheLoadFailed", "Could not support development." }, { "kAMDMuxConnectError", "Could not connect to the device." }, { "kAMDMuxCreateListenerError", "Could not listen for USB devices." }, { "kAMDMuxError", "There was an error with the USB device multiplexor." }, { "kAMDMuxGetListenerError", "Could not get the USB listener." }, { "kAMDNewsstandArtworkCaptureFailedError", "Could not save the Newsstand artwork." }, { "kAMDNoResourcesError", "Could not allocate a resource." }, { "kAMDNoSpaceError", "No space is available on the device." }, { "kAMDNoWifiSyncSupportError", "Device doesn't support wireless sync." }, { "kAMDNotConnectedError", "Not connected to the device." }, { "kAMDNotEntitledError", "The requesting application is not allowed to make this request." }, { "kAMDNotFoundError", "The file could not be found." }, { "kAMDNotificationFailedError", "Could not post a notification." }, { "kAMDOverrunError", "There was a buffer overrun." }, { "kAMDPackageExtractionFailedError", "Could not open the application package." }, { "kAMDPackageInspectionFailedError", "Could not inspect the application package." }, { "kAMDPackageMoveFailedError", "Could not move the application package into the staging location." }, { "kAMDPackagePatchFailedError", "Could not apply patch update to application." }, { "kAMDPairingDialogResponsePendingError", "The user has not yet responded to the pairing request." }, { "kAMDPairingProhibitedError", "Pairing only allowed over USB." }, { "kAMDPasswordProtectedError", "The device is passcode protected." }, { "kAMDPasswordRequiredError", "A passcode is required to be set on the device." }, { "kAMDPathConversionFailedError", "Could not convert the path." }, { "kAMDPermissionError", "You do not have permission." }, { "kAMDPluginCopyFailedError", "Could not copy VPN Plugin into app container." }, { "kAMDProhibitedBySupervision", "Operation prohibited on supervised devices." }, { "kAMDProvisioningProfileNotValid", "The provisioning profile is not valid." }, { "kAMDReadError", "Could not read from the device." }, { "kAMDReceiveMessageError", "Could not receive a message from the device." }, { "kAMDRemoveProhibitedError", "Cannot remove value on device." }, { "kAMDRequireCUPairingBackoffError", "Retry later." }, { "kAMDRequireCUPairingCodeError", "Invalid PIN code entered." }, { "kAMDRequireCUPairingError", "Cannot pair over network yet" }, { "kAMDRestoreContainerFailedError", "Could not restore the application container." }, { "kAMDSUFirmwareError", "Could not flash the firmware." }, { "kAMDSUPatchError", "Could not patch the file." }, { "kAMDSUVerificationError", "The software update package could not be verified." }, { "kAMDSavePairRecordFailedError", "Could not save the pairing record." }, { "kAMDSeatbeltProfileRemovalFailedError", "Could not remove the application seatbelt profile." }, { "kAMDSendMessageError", "Could not send a message to the device." }, { "kAMDServiceLimitError", "Too many instances of this service are already running." }, { "kAMDServiceProhibitedError", "The service could not be started on the device." }, { "kAMDSessionActiveError", "The session is active." }, { "kAMDSessionInactiveError", "The session is inactive." }, { "kAMDSetProhibitedError", "Cannot set value on device." }, { "kAMDStageCreationFailedError", "Could not create the staging directory." }, { "kAMDStartServiceError", "The service could not be started." }, { "kAMDSuccess", "There was no error." }, { "kAMDSymlinkFailedError", "Could not create the symlink." }, { "kAMDTimeOutError", "The operation timed out." }, { "kAMDTooBigError", "The message is too big." }, { "kAMDUndefinedError", "An unknown error occurred." }, { "kAMDUninstallProhibitedError", "Uninstallation of apps is prohibited by a policy on the device." }, { "kAMDUnknownCommandError", "The device does not recognize the command." }, { "kAMDUnknownPacketError", "The packet is unknown." }, { "kAMDUnsupportedError", "This operation is unsupported." }, { "kAMDUserDeniedPairingError", "The device rejected the pairing attempt." }, { "kAMDWriteError", "Could not write to the device." }, { "kAMDWrongDroidError", "The device is in recovery mode." }, { "kAMDiTunesArtworkCaptureFailedError", "Could not save the iTunes artwork." }, { "kAMDiTunesMetadataCaptureFailedError", "Could not save the iTunes metadata." }, { "kMobileHouseArrestApplicationLookupFailed", "The requested application is not a user application." }, { "kMobileHouseArrestDictionaryFailed", "The request contained an invalid request dictionary." }, { "kMobileHouseArrestInstallationLookupFailed", "Could not find the requested application." }, { "kMobileHouseArrestInvalidPath", "The requested application contained an invalid data container path." }, { "kMobileHouseArrestMissingCommand", "The request was missing a command." }, { "kMobileHouseArrestMissingContainer", "The requested application does not contain a valid data container." }, { "kMobileHouseArrestMissingIdentifier", "The request was missing an application identifier." }, { "kMobileHouseArrestPathConversionFailed", "Could not convert the requested application's data container path." }, { "kMobileHouseArrestPathMissing", "The requested application's data container path does not exist." }, { "kMobileHouseArrestUnknownCommand", "The request contained an invalid command." }, };

const int error_id_to_message_count = sizeof(error_id_to_message) / sizeof(error_id_to_message_t);

const char get_error_message(unsigned int error) { const char id = NULL;

// this creates an error code to match what's defined in `errorcode_to_id`;
// taken from https://github.com/samdmarshall/SDMMobileDevice/blob/c3e1e97b1310c7a7a10f68281752760038b75e16/Framework/include/SDMMobileDevice/SDMMD_Error.h#L512
// note that the `error & 0xff` isn't done here because there are defined errors like `0xe8008001`
const unsigned int error_code = error | 0xe8000000;

// Lookup error localization id
for (int i = 0; i < errorcode_to_id_count; i++) {
    if (errorcode_to_id[i].error == error_code) {
        id = errorcode_to_id[i].id;
        break;
    }
}

// Lookup error message
if (id) {
    for (int i = 0; i < error_id_to_message_count; i++)
        if (strcmp(error_id_to_message[i].id, id) == 0)
            return error_id_to_message[i].message;
}

// If message is not found, then at least return id if it was found, otherwise NULL
return id;

};

Yonita34567 commented 2 months ago

!/bin/sh

if [ -z "$1" ] then echo "Usage: $0 [version_to_set]" exit 1 fi

echo "\"$1\"" > src/ios-deploy/version.h npm version --no-git-tag-version $1

Yonita34567 commented 2 months ago

//TODO: don't copy/mount DeveloperDiskImage.dmg if it's already done - Xcode checks this somehow

import <CoreFoundation/CoreFoundation.h>

import <Foundation/Foundation.h>

include

include <sys/mman.h>

include <sys/socket.h>

include <sys/types.h>

include <sys/stat.h>

include <sys/un.h>

include <sys/sysctl.h>

include

include

include

include

include

include

include <netinet/in.h>

include <netinet/tcp.h>

include "MobileDevice.h"

import "errors.h"

import "device_db.h"

define PREP_CMDS_PATH @"/tmp/%@/fruitstrap-lldb-prep-cmds-"

define LLDB_SHELL @"PATH=/usr/bin /usr/bin/lldb -s %@"

/*

const char* lldb_prep_no_cmds = "";

const char* lldb_prep_interactive_cmds = "\ run\n\ ";

const char* lldb_prep_noninteractive_justlaunch_cmds = "\ run\n\ safequit\n\ ";

const char* lldb_prep_noninteractive_cmds = "\ run\n\ autoexit\n\ ";

NSMutableString * custom_commands = nil;

/*

const char output_path = NULL; const char error_path = NULL;

typedef struct am_device AMDeviceRef; mach_error_t AMDeviceSecureStartService(AMDeviceRef device, CFStringRef service_name, unsigned int unknown, ServiceConnRef handle); mach_error_t AMDeviceCreateHouseArrestService(AMDeviceRef device, CFStringRef identifier, CFDictionaryRef options, AFCConnectionRef handle); CFSocketNativeHandle AMDServiceConnectionGetSocket(ServiceConnRef con); void AMDServiceConnectionInvalidate(ServiceConnRef con);

bool AMDeviceIsAtLeastVersionOnPlatform(AMDeviceRef device, CFDictionaryRef vers); int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void callback, int cbarg); int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void callback, int cbarg); int AMDeviceSecureInstallApplicationBundle(AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void callback, int cbarg); int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void callback, int cbarg); mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result); int AMDeviceGetInterfaceType(AMDeviceRef device); AMDeviceRef AMDeviceCopyPairedCompanion(AMDeviceRef device);

if defined(IOS_DEPLOY_FEATURE_DEVELOPER_MODE)

unsigned int AMDeviceCopyDeveloperModeStatus(AMDeviceRef device, uint32_t *error_code);

endif

int AMDServiceConnectionSend(ServiceConnRef con, const void data, size_t size); int AMDServiceConnectionReceive(ServiceConnRef con, void data, size_t size); uint64_t AMDServiceConnectionReceiveMessage(ServiceConnRef con, CFPropertyListRef message, CFPropertyListFormat format); uint64_t AMDServiceConnectionSendMessage(ServiceConnRef con, CFPropertyListRef message, CFPropertyListFormat format); CFArrayRef AMDeviceCopyProvisioningProfiles(AMDeviceRef device); int AMDeviceInstallProvisioningProfile(AMDeviceRef device, void profile); int AMDeviceRemoveProvisioningProfile(AMDeviceRef device, CFStringRef uuid); CFStringRef MISProfileGetValue(void profile, CFStringRef key); CFDictionaryRef MISProfileCopyPayload(void profile); void MISProfileCreateWithData(int zero, CFDataRef data); int MISProfileWriteToFile(void profile, CFStringRef dest_path);

bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, debugserver_only = false, detect_only = false, install = true, uninstall = false, no_wifi = false; bool faster_path_search = false; bool command_only = false; char command = NULL; char consttarget_filename = NULL; char constupload_pathname = NULL; char bundle_id = NULL; NSMutableArray keys = NULL; bool interactive = true; bool justlaunch = false; bool file_system = false; bool non_recursively = false; char app_path = NULL; char app_deltas = NULL; char device_id = NULL; char args = NULL; char envs = NULL; char list_root = NULL; const char custom_script_path = NULL; char symbols_download_directory = NULL; char profile_uuid = NULL; char profile_path = NULL; int command_pid = -1; int _timeout = 0; int _detectDeadlockTimeout = 0; bool _json_output = false; NSMutableArray _file_meta_info = nil; int port = 0; // 0 means "dynamically assigned" pid_t parent = 0; // PID of child process running lldb pid_t child = 0; // Signal sent from child to parent process when LLDB finishes. const int SIGLLDB = SIGUSR1; NSString tmpUUID; struct am_device_notification notify; CFRunLoopSourceRef fdvendor_runloop;

CFMutableDictionaryRef debugserver_active_connections;

uint32_t symbols_file_paths_command = 0x30303030; uint32_t symbols_download_file_command = 0x01000000; CFStringRef symbols_service_name = CFSTR("com.apple.dt.fetchsymbols"); const int symbols_logging_interval_ms = 250;

const size_t sizeof_uint32_t = sizeof(uint32_t);

// Error codes we report on different failures, so scripts can distinguish between user app exit // codes and our exit codes. For non app errors we use codes in reserved 128-255 range. const int exitcode_timeout = 252; const int exitcode_error = 253; const int exitcode_app_crash = 254;

// Checks for MobileDevice.framework errors, tries to print them and exits.

define check_error(call) \

do {                                                                        \
    unsigned int err = (unsigned int)call;                                  \
    if (err != 0)                                                           \
    {                                                                       \
        const char* msg = get_error_message(err);                           \
        NSString *description = msg ? [NSString stringWithUTF8String:msg] : @"unknown."; \
        NSLogJSON(@{@"Event": @"Error", @"Code": @(err), @"Status": description}); \
        on_error(@"Error 0x%x: %@ " #call, err, description);               \
    }                                                                       \
} while (false);

// Checks for MobileDevice.framework errors and tries to print them.

define log_error(call) \

do {                                                                        \
    unsigned int err = (unsigned int)call;                                  \
    if (err != 0)                                                           \
    {                                                                       \
        const char* msg = get_error_message(err);                           \
        NSString *description = msg ? [NSString stringWithUTF8String:msg] : @"unknown."; \
        NSLogJSON(@{@"Event": @"Error", @"Code": @(err), @"Status": description}); \
        log_on_error(@"Error 0x%x: %@ " #call, err, description);               \
    }                                                                       \
} while (false);

void disable_ssl(ServiceConnRef con) { // MobileDevice links with SSL, so function will be available; typedef void (SSL_free_t)(void); static SSL_free_t SSL_free = NULL; if (SSL_free == NULL) { SSL_free = (SSL_free_t)dlsym(RTLD_DEFAULT, "SSL_free"); }

SSL_free(con->sslContext);
con->sslContext = NULL;

}

void log_on_error(NSString format, ...) { va_list valist; va_start(valist, format); NSString str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease]; va_end(valist);

if (!_json_output) {
    NSLog(@"[ !! ] %@", str);
}

}

void on_error(NSString format, ...) { va_list valist; va_start(valist, format); NSString str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease]; va_end(valist);

if (!_json_output) {
    NSLog(@"[ !! ] %@", str);
}

exit(exitcode_error);

}

// Print error message getting last errno and exit void on_sys_error(NSString format, ...) { const char errstr = strerror(errno);

va_list valist;
va_start(valist, format);
NSString* str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease];
va_end(valist);

on_error(@"%@ : %@", str, [NSString stringWithUTF8String:errstr]);

}

void __NSLogOut(NSString format, va_list valist) { NSString str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease]; [[str stringByAppendingString:@"\n"] writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil]; }

void NSLogOut(NSString* format, ...) { if (!_json_output) { va_list valist; va_start(valist, format); __NSLogOut(format, valist); va_end(valist); } }

void NSLogVerbose(NSString* format, ...) { if (verbose && !_json_output) { va_list valist; va_start(valist, format); __NSLogOut(format, valist); va_end(valist); } }

void NSLogJSON(NSDictionary jsonDict) { if (_json_output) { NSError error; NSData data = [NSJSONSerialization dataWithJSONObject:jsonDict options:NSJSONWritingPrettyPrinted error:&error]; if (data) { NSString jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; [jsonString writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil]; [jsonString release]; } else { [@"{\"JSONError\": \"JSON error\"}" writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil]; } } }

uint64_t get_current_time_in_milliseconds() { return clock_gettime_nsec_np(CLOCK_REALTIME) / (1000 * 1000); }

BOOL mkdirp(NSString path) { NSError error = nil; BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; return success; }

Boolean path_exists(CFTypeRef path) { if (CFGetTypeID(path) == CFStringGetTypeID()) { CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, true); Boolean result = CFURLResourceIsReachable(url, NULL); CFRelease(url); return result; } else if (CFGetTypeID(path) == CFURLGetTypeID()) { return CFURLResourceIsReachable(path, NULL); } else { return false; } }

CFStringRef copy_find_path(CFStringRef rootPath, CFStringRef namePattern) { FILE *fpipe = NULL; CFStringRef cf_command;

if( !path_exists(rootPath) )
    return NULL;

if (faster_path_search) {
    CFIndex maxdepth = 1;
    CFArrayRef findPathSlash = CFStringCreateArrayWithFindResults(NULL, namePattern, CFSTR("/"), CFRangeMake(0, CFStringGetLength(namePattern)), 0);
    if (findPathSlash != NULL) {
        maxdepth = CFArrayGetCount(findPathSlash) + 1;
        CFRelease(findPathSlash);
    }

    cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find '%@' -path '%@/%@' -maxdepth %ld 2>/dev/null | sort | tail -n 1"), rootPath, rootPath, namePattern, maxdepth);
}
else {
    if (CFStringFind(namePattern, CFSTR("*"), 0).location == kCFNotFound) {
        //No wildcards. Let's speed up the search
        CFStringRef path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), rootPath, namePattern);

        if( path_exists(path) )
            return path;

        CFRelease(path);
        return NULL;
    }

    if (CFStringFind(namePattern, CFSTR("/"), 0).location == kCFNotFound) {
        cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find '%@' -name '%@' -maxdepth 1 2>/dev/null | sort | tail -n 1"), rootPath, namePattern);
    } else {
        cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find '%@' -path '%@/%@' 2>/dev/null | sort | tail -n 1"), rootPath, rootPath, namePattern);
    }
}

char command[1024] = { '\0' };
CFStringGetCString(cf_command, command, sizeof(command), kCFStringEncodingUTF8);
CFRelease(cf_command);

if (!(fpipe = (FILE *)popen(command, "r")))
    on_sys_error(@"Error encountered while opening pipe");

char buffer[256] = { '\0' };

fgets(buffer, sizeof(buffer), fpipe);
pclose(fpipe);

strtok(buffer, "\n");

CFStringRef path = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);

if( CFStringGetLength(path) > 0 && path_exists(path) )
    return path;

CFRelease(path);
return NULL;

}

CFStringRef copy_xcode_dev_path(void) { static char xcode_dev_path[256] = { '\0' }; if (strlen(xcode_dev_path) == 0) { const char* env_dev_path = getenv("DEVELOPER_DIR");

    if (env_dev_path && strlen(env_dev_path) > 0) {
        strcpy(xcode_dev_path, env_dev_path);
        // DEVELOPER_DIR should refer to Xcode.app/Contents/Developer, but
        // xcode-select and friends have an extension to fix the path, if it points to Xcode.app/.
        static char dev_subdir[256] = { '\0' };
        strcat(strcat(dev_subdir, env_dev_path), "/Contents/Developer");
        struct stat sb;
        if (stat(dev_subdir, &sb) == 0)
        {
            strcpy(xcode_dev_path, dev_subdir);
        }
    } else {
        FILE *fpipe = NULL;
        char *command = "xcode-select -print-path";

        if (!(fpipe = (FILE *)popen(command, "r")))
            on_sys_error(@"Error encountered while opening pipe");

        char buffer[256] = { '\0' };

        fgets(buffer, sizeof(buffer), fpipe);
        pclose(fpipe);

        strtok(buffer, "\n");
        strcpy(xcode_dev_path, buffer);
    }
    NSLogVerbose(@"Found Xcode developer dir %s", xcode_dev_path);
}
return CFStringCreateWithCString(NULL, xcode_dev_path, kCFStringEncodingUTF8);

}

const char get_home(void) { const char home = getenv("HOME"); if (!home) { struct passwd *pwd = getpwuid(getuid()); home = pwd->pw_dir; } return home; }

CFStringRef copy_xcode_path_for_impl(CFStringRef rootPath, CFStringRef subPath, CFStringRef search) { CFStringRef searchPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), rootPath, subPath ); CFStringRef res = copy_find_path(searchPath, search); CFRelease(searchPath); return res; }

CFStringRef copy_xcode_path_for(CFStringRef subPath, CFStringRef search) { CFStringRef xcodeDevPath = copy_xcode_dev_path(); CFStringRef defaultXcodeDevPath = CFSTR("/Applications/Xcode.app/Contents/Developer"); CFStringRef path = NULL; const char* home = get_home();

// Try using xcode-select --print-path
path = copy_xcode_path_for_impl(xcodeDevPath, subPath, search);

// If not look in the default xcode location (xcode-select is sometimes wrong)
if (path == NULL && CFStringCompare(xcodeDevPath, defaultXcodeDevPath, 0) != kCFCompareEqualTo )
    path = copy_xcode_path_for_impl(defaultXcodeDevPath, subPath, search);

// If not look in the users home directory, Xcode can store device support stuff there
if (path == NULL) {
    CFRelease(xcodeDevPath);
    xcodeDevPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode"), home );
    path = copy_xcode_path_for_impl(xcodeDevPath, subPath, search);
}

CFRelease(xcodeDevPath);

return path;

}

device_desc get_device_desc(CFStringRef model) { if (model != NULL) { size_t sz = sizeof(device_db) / sizeof(device_desc); for (size_t i = 0; i < sz; i ++) { if (CFStringCompare(model, device_db[i].model, kCFCompareNonliteral | kCFCompareCaseInsensitive) == kCFCompareEqualTo) { return device_db[i]; } } }

device_desc res = device_db[UNKNOWN_DEVICE_IDX];

res.model = model;
res.name = model;

return res;

}

bool is_usb_device(const AMDeviceRef device) { return AMDeviceGetInterfaceType(device) == 1; }

void connect_and_start_session(AMDeviceRef device) { AMDeviceConnect(device); assert(AMDeviceIsPaired(device)); check_error(AMDeviceValidatePairing(device)); check_error(AMDeviceStartSession(device)); }

CFStringRef get_device_full_name(const AMDeviceRef device) { CFStringRef full_name = NULL, device_udid = AMDeviceCopyDeviceIdentifier(device), device_name = NULL, model_name = NULL, sdk_name = NULL, arch_name = NULL, product_version = NULL, build_version = NULL;

AMDeviceConnect(device);

device_name = AMDeviceCopyValue(device, 0, CFSTR("DeviceName"));

// Please ensure that device is connected or the name will be unknown
CFStringRef model = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
device_desc dev;
if (model != NULL) {
    dev = get_device_desc(model);
} else {
    dev= device_db[UNKNOWN_DEVICE_IDX];
    model = dev.model;
}
model_name = dev.name;
sdk_name = dev.sdk;
arch_name = dev.arch;
product_version = AMDeviceCopyValue(device, 0, CFSTR("ProductVersion"));
build_version = AMDeviceCopyValue(device, 0, CFSTR("BuildVersion"));

NSLogVerbose(@"Hardware Model: %@", model);
NSLogVerbose(@"Device Name: %@", device_name);
NSLogVerbose(@"Model Name: %@", model_name);
NSLogVerbose(@"SDK Name: %@", sdk_name);
NSLogVerbose(@"Architecture Name: %@", arch_name);
NSLogVerbose(@"Product Version: %@", product_version);
NSLogVerbose(@"Build Version: %@", build_version);
if (build_version == 0)
    build_version = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);

if (device_name != NULL) {
    full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@, %@, %@, %@, %@, %@) a.k.a. '%@'"), device_udid, model, model_name, sdk_name, arch_name, product_version, build_version, device_name);
} else {
    full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@, %@, %@, %@, %@, %@)"), device_udid, model, model_name, sdk_name, arch_name, product_version, build_version);
}

AMDeviceDisconnect(device);

if(device_udid != NULL)
    CFRelease(device_udid);
if(device_name != NULL)
    CFRelease(device_name);
if(model != NULL)
    CFRelease(model);
if(model_name != NULL && model_name != model)
    CFRelease(model_name);
if(product_version)
    CFRelease(product_version);
if(build_version)
    CFRelease(build_version);

return CFAutorelease(full_name);

}

NSDictionary get_device_json_dict(const AMDeviceRef device) { NSMutableDictionary json_dict = [NSMutableDictionary new]; is_usb_device(device) ? AMDeviceConnect(device) : connect_and_start_session(device);

CFStringRef device_udid = AMDeviceCopyDeviceIdentifier(device);
if (device_udid) {
    [json_dict setValue:(__bridge NSString *)device_udid forKey:@"DeviceIdentifier"];
    CFRelease(device_udid);
}

CFStringRef device_hardware_model = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
if (device_hardware_model) {
    [json_dict setValue:(NSString*)device_hardware_model forKey:@"HardwareModel"];
    size_t device_db_length = sizeof(device_db) / sizeof(device_desc);
    for (size_t i = 0; i < device_db_length; i ++) {
        if (CFStringCompare(device_hardware_model, device_db[i].model, kCFCompareNonliteral | kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
            device_desc dev = device_db[i];
            [json_dict setValue:(__bridge NSString *)dev.name forKey:@"modelName"];
            [json_dict setValue:(__bridge NSString *)dev.sdk forKey:@"modelSDK"];
            [json_dict setValue:(__bridge NSString *)dev.arch forKey:@"modelArch"];
            break;
        }
    }
    CFRelease(device_hardware_model);
}

for (NSString *deviceValue in @[@"DeviceName",
                                @"BuildVersion",
                                @"DeviceClass",
                                @"ProductType",
                                @"ProductVersion"]) {
    CFStringRef cf_value = AMDeviceCopyValue(device, 0, (__bridge CFStringRef)deviceValue);
    if (cf_value) {
        [json_dict setValue:(__bridge NSString *)cf_value forKey:deviceValue];
        CFRelease(cf_value);
    }
}

AMDeviceDisconnect(device);

return CFAutorelease(json_dict);

}

int get_companion_interface_type(AMDeviceRef device) { assert(AMDeviceGetInterfaceType(device) == 3); AMDeviceRef companion = AMDeviceCopyPairedCompanion(device); int type = AMDeviceGetInterfaceType(companion); AMDeviceRelease(companion); return type; }

CFStringRef get_device_interface_name(const AMDeviceRef device) { // AMDeviceGetInterfaceType(device) 0=Unknown, 1 = Direct/USB, 2 = Indirect/WIFI, 3 = Companion proxy switch(AMDeviceGetInterfaceType(device)) { case 1: return CFSTR("USB"); case 2: return CFSTR("WIFI"); case 3: { if (get_companion_interface_type(device) == 1) { return CFSTR("USB Companion proxy"); } else { return CFSTR("WIFI Companion proxy"); } } default: return CFSTR("Unknown Connection"); } }

CFMutableArrayRef copy_device_product_version_parts(AMDeviceRef device) { CFStringRef version = AMDeviceCopyValue(device, 0, CFSTR("ProductVersion")); CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(NULL, version, CFSTR(".")); CFMutableArrayRef result = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(parts), parts); CFRelease(version); CFRelease(parts); return result; }

CFStringRef copy_device_support_path(AMDeviceRef device, CFStringRef suffix) { time_t startTime, endTime; time( &startTime );

CFStringRef version = NULL;
CFStringRef build = AMDeviceCopyValue(device, 0, CFSTR("BuildVersion"));
CFStringRef deviceClass = AMDeviceCopyValue(device, 0, CFSTR("DeviceClass"));
CFStringRef deviceModel = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
CFStringRef productType = AMDeviceCopyValue(device, 0, CFSTR("ProductType"));
CFStringRef deviceArch = NULL;
CFStringRef path = NULL;

device_desc dev;
if (deviceModel != NULL) {
    dev = get_device_desc(deviceModel);
    deviceArch = dev.arch;
}

CFMutableArrayRef version_parts = copy_device_product_version_parts(device);

NSLogVerbose(@"Device Class: %@", deviceClass);
NSLogVerbose(@"build: %@", build);

CFStringRef deviceClassPath[2];

if (CFStringCompare(CFSTR("AppleTV"), deviceClass, 0) == kCFCompareEqualTo) {
  deviceClassPath[0] = CFSTR("Platforms/AppleTVOS.platform/DeviceSupport");
  deviceClassPath[1] = CFSTR("tvOS DeviceSupport");
}
else if (CFStringCompare(CFSTR("Watch"), deviceClass, 0) == kCFCompareEqualTo) {
  deviceClassPath[0] = CFSTR("Platforms/WatchOS.platform/DeviceSupport");
  deviceClassPath[1] = CFSTR("watchOS DeviceSupport");
}
else {
  deviceClassPath[0] = CFSTR("Platforms/iPhoneOS.platform/DeviceSupport");
  deviceClassPath[1] = CFSTR("iOS DeviceSupport");
}

CFMutableArrayRef string_allocations = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
while (CFArrayGetCount(version_parts) > 0) {
    version = CFStringCreateByCombiningStrings(NULL, version_parts, CFSTR("."));
    NSLogVerbose(@"version: %@", version);

    for( int i = 0; i < 2; ++i ) {
        if (path == NULL) {
            CFStringRef search = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@) %@/%@"), version, build, deviceArch, suffix);
            path = copy_xcode_path_for(deviceClassPath[i], search);
            CFRelease(search);
        }

        if (path == NULL) {
            CFStringRef search = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)/%@"), version, build, suffix);
            path = copy_xcode_path_for(deviceClassPath[i], search);
            CFRelease(search);
        }

        if (path == NULL) {
            CFStringRef search = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (*)/%@"), version, suffix);
            path = copy_xcode_path_for(deviceClassPath[i], search);
            CFRelease(search);
        }

        if (path == NULL) {
            CFStringRef search = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), version, suffix);
            path = copy_xcode_path_for(deviceClassPath[i], search);
            CFRelease(search);
        }

        if (path == NULL) {
            CFStringRef search = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.*/%@"), version, suffix);
            path = copy_xcode_path_for(deviceClassPath[i], search);
            CFRelease(search);
        }
        if (path == NULL) {
            CFStringRef search = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ %@ (%@)/%@"), productType, version, build, suffix);
            path = copy_xcode_path_for(deviceClassPath[i], search);
            CFRelease(search);
        }
    }

    CFRelease(version);
    if (path != NULL) {
        break;
    }

    // Not all iOS versions have a dedicated developer disk image. Xcode 13.4.1 supports
    // iOS up to 15.5 but does not include developer disk images for 15.1 or 15.3
    // despite being able to deploy to them. For this reason, this logic looks for previous
    // minor versions if it doesn't find an exact match. In the case where the disk image
    // from a previous minor version is not compatible, deployment will fail with
    // kAMDInvalidServiceError.
    CFStringRef previous_minor_version = NULL;
    if (CFEqual(CFSTR("DeveloperDiskImage.dmg"), suffix) &&
        CFArrayGetCount(version_parts) == 2) {
        int minor_version = CFStringGetIntValue(CFArrayGetValueAtIndex(version_parts, 1));
        if (minor_version > 0) {
            previous_minor_version = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
                                                              CFSTR("%d"), minor_version - 1);
            CFArrayAppendValue(string_allocations, previous_minor_version);
        }
    }
    CFArrayRemoveValueAtIndex(version_parts, CFArrayGetCount(version_parts) - 1);
    if (previous_minor_version) {
        CFArrayAppendValue(version_parts, previous_minor_version);
    }
}

for (int i = 0; i < CFArrayGetCount(string_allocations); i++) {
    CFRelease(CFArrayGetValueAtIndex(string_allocations, i));
}
CFRelease(string_allocations);

for( int i = 0; i < 2; ++i ) {
    if (path == NULL) {
        CFStringRef search = CFStringCreateWithFormat(NULL, NULL, CFSTR("Latest/%@"), suffix);
        path = copy_xcode_path_for(deviceClassPath[i], search);
        CFRelease(search);
    }
}

CFRelease(version_parts);
CFRelease(build);
CFRelease(deviceClass);
CFRelease(productType);
if (deviceModel != NULL) {
    CFRelease(deviceModel);
}
if (path == NULL) {
  NSString *msg = [NSString stringWithFormat:@"Unable to locate DeviceSupport directory with suffix '%@'. This probably means you don't have Xcode installed, you will need to launch the app manually and logging output will not be shown!", suffix];
    NSLogJSON(@{
      @"Event": @"DeviceSupportError",
      @"Status": msg,
    });
    on_error(msg);
}

time( &endTime );
NSLogVerbose(@"DeviceSupport directory '%@' was located. It took %.2f seconds", path, difftime(endTime,startTime));

return path;

}

void mount_callback(CFDictionaryRef dict, int arg) { CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));

if (CFEqual(status, CFSTR("LookingUpImage"))) {
    NSLogOut(@"[  0%%] Looking up developer disk image");
} else if (CFEqual(status, CFSTR("CopyingImage"))) {
    NSLogOut(@"[ 30%%] Copying DeveloperDiskImage.dmg to device");
} else if (CFEqual(status, CFSTR("MountingImage"))) {
    NSLogOut(@"[ 90%%] Mounting developer disk image");
}

}

void mount_developer_image(AMDeviceRef device) { CFStringRef image_path = copy_device_support_path(device, CFSTR("DeveloperDiskImage.dmg")); CFStringRef sig_path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.signature"), image_path);

NSLogVerbose(@"Developer disk image: %@", image_path);

FILE* sig = fopen(CFStringGetCStringPtr(sig_path, kCFStringEncodingMacRoman), "rb");
size_t buf_size = 128;
void *sig_buf = malloc(buf_size);
size_t bytes_read = fread(sig_buf, 1, buf_size, sig);
if (bytes_read != buf_size) {
  on_sys_error(@"fread read %d bytes but expected %d bytes.", bytes_read, buf_size);
}
fclose(sig);
CFDataRef sig_data = CFDataCreateWithBytesNoCopy(NULL, sig_buf, buf_size, NULL);
CFRelease(sig_path);

CFTypeRef keys[] = { CFSTR("ImageSignature"), CFSTR("ImageType") };
CFTypeRef values[] = { sig_data, CFSTR("Developer") };
CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(sig_data);

unsigned int result = (unsigned int)AMDeviceMountImage(device, image_path, options, &mount_callback, 0);
if (result == 0) {
    NSLogOut(@"[ 95%%] Developer disk image mounted successfully");
} else if (result == 0xe8000076 /* already mounted */) {
    NSLogOut(@"[ 95%%] Developer disk image already mounted");
} else {
    if (result != 0) {
        const char* msg = get_error_message(result);
        NSString *description = @"unknown.";
        if (msg) {
            description = [NSString stringWithUTF8String:msg];
            NSLogOut(@"Error: %@", description);
        }
        NSLogJSON(@{@"Event": @"Error",
                    @"Code": @(result),
                    @"Status": description});
    }

    on_error(@"Unable to mount developer disk image. (%x)", result);
}

CFStringRef symbols_path = copy_device_support_path(device, CFSTR("Symbols"));
if (symbols_path != NULL)
{
    NSLogOut(@"Symbol Path: %@", symbols_path);
    NSLogJSON(@{@"Event": @"MountDeveloperImage",
                @"SymbolsPath": (__bridge NSString *)symbols_path
                });
    CFRelease(symbols_path);
}

CFRelease(image_path);
CFRelease(options);

}

mach_error_t transfer_callback(CFDictionaryRef dict, int arg) { if (CFDictionaryGetValue(dict, CFSTR("Error"))) { return 0; } int percent; CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status")); CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);

if (CFEqual(status, CFSTR("CopyingFile"))) {
    static CFStringRef last_path = NULL;
    static int last_overall_percent = -1;

    CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path"));
    int overall_percent = percent / 2;

    if ((last_path == NULL || !CFEqual(path, last_path) || last_overall_percent != overall_percent) && !CFStringHasSuffix(path, CFSTR(".ipa"))) {
        NSLogOut(@"[%3d%%] Copying %@ to device", overall_percent, path);
        NSLogJSON(@{@"Event": @"BundleCopy",
                    @"OverallPercent": @(overall_percent),
                    @"Percent": @(percent),
                    @"Path": (__bridge NSString *)path
                    });
    }

    last_overall_percent = overall_percent;

    if (last_path != NULL) {
        CFRelease(last_path);
    }
    last_path = CFStringCreateCopy(NULL, path);
}

return 0;

}

mach_error_t install_callback(CFDictionaryRef dict, int arg) { if (CFDictionaryGetValue(dict, CFSTR("Error"))) { return 0; } int percent; CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status")); CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);

int overall_percent = (percent / 2) + 50;
NSLogOut(@"[%3d%%] %@", overall_percent, status);
NSLogJSON(@{@"Event": @"BundleInstall",
            @"OverallPercent": @(overall_percent),
            @"Percent": @(percent),
            @"Status": (__bridge NSString *)status
            });
return 0;

}

// During standard installation transferring and installation takes place // in distinct function that can be passed distinct callbacks. Incremental // installation performs both transfer and installation in a single function so // use this callback to determine which step is occuring and call the proper // callback. mach_error_t incremental_install_callback(CFDictionaryRef dict, int arg) { if (CFDictionaryGetValue(dict, CFSTR("Error"))) { return 0; } CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status")); if (CFEqual(status, CFSTR("TransferringPackage"))) { int percent; CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent); int overall_percent = (percent / 2); NSLogOut(@"[%3d%%] %@", overall_percent, status); NSLogJSON(@{@"Event": @"TransferringPackage", @"OverallPercent": @(overall_percent), }); return 0; } else if (CFEqual(status, CFSTR("CopyingFile"))) { return transfer_callback(dict, arg); } else { return install_callback(dict, arg); } }

CFURLRef copy_device_app_url(AMDeviceRef device, CFStringRef identifier) { CFDictionaryRef result = nil;

NSArray *a = [NSArray arrayWithObjects:
              @"CFBundleIdentifier",            // absolute must
              @"ApplicationDSID",
              @"ApplicationType",
              @"CFBundleExecutable",
              @"CFBundleDisplayName",
              @"CFBundleIconFile",
              @"CFBundleName",
              @"CFBundleShortVersionString",
              @"CFBundleSupportedPlatforms",
              @"CFBundleURLTypes",
              @"CodeInfoIdentifier",
              @"Container",
              @"Entitlements",
              @"HasSettingsBundle",
              @"IsUpgradeable",
              @"MinimumOSVersion",
              @"Path",
              @"SignerIdentity",
              @"UIDeviceFamily",
              @"UIFileSharingEnabled",
              @"UIStatusBarHidden",
              @"UISupportedInterfaceOrientations",
              nil];

NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
CFDictionaryRef options = (CFDictionaryRef)optionsDict;

check_error(AMDeviceLookupApplications(device, options, &result));

CFDictionaryRef app_dict = CFDictionaryGetValue(result, identifier);
assert(app_dict != NULL);

CFStringRef app_path = CFDictionaryGetValue(app_dict, CFSTR("Path"));
assert(app_path != NULL);

CFURLRef url = CFURLCreateWithFileSystemPath(NULL, app_path, kCFURLPOSIXPathStyle, true);
CFRelease(result);
return url;

}

CFStringRef copy_disk_app_identifier(CFURLRef disk_app_url) { CFURLRef plist_url = CFURLCreateCopyAppendingPathComponent(NULL, disk_app_url, CFSTR("Info.plist"), false); CFReadStreamRef plist_stream = CFReadStreamCreateWithFile(NULL, plist_url); if (!CFReadStreamOpen(plist_stream)) { on_error(@"Cannot read Info.plist file: %@", plist_url); }

CFPropertyListRef plist = CFPropertyListCreateWithStream(NULL, plist_stream, 0, kCFPropertyListImmutable, NULL, NULL);
CFStringRef bundle_identifier = CFRetain(CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier")));
CFReadStreamClose(plist_stream);

CFRelease(plist_url);
CFRelease(plist_stream);
CFRelease(plist);

return bundle_identifier;

}

CFStringRef copy_modules_search_paths_pairs(CFStringRef symbols_path, CFStringRef disk_container, CFStringRef device_container_private, CFStringRef device_container_noprivate ) { CFMutableStringRef res = CFStringCreateMutable(kCFAllocatorDefault, 0); CFStringAppendFormat(res, NULL, CFSTR("/usr \"%@/usr\""), symbols_path); CFStringAppendFormat(res, NULL, CFSTR(" /System \"%@/System\""), symbols_path); CFStringAppendFormat(res, NULL, CFSTR(" \"%@\" \"%@\""), device_container_private, disk_container); CFStringAppendFormat(res, NULL, CFSTR(" \"%@\" \"%@\""), device_container_noprivate, disk_container); CFStringAppendFormat(res, NULL, CFSTR(" /Developer \"%@/Developer\""), symbols_path);

return res;

}

CFStringRef get_device_platform(AMDeviceRef device) { CFStringRef deviceClass = AMDeviceCopyValue(device, 0, CFSTR("DeviceClass")); CFStringRef platform; if (CFStringCompare(CFSTR("AppleTV"), deviceClass, 0) == kCFCompareEqualTo) { platform = CFSTR("tvos"); } else if (CFStringCompare(CFSTR("Watch"), deviceClass, 0) == kCFCompareEqualTo) { platform = CFSTR("watchos"); } else { platform = CFSTR("ios"); } CFRelease(deviceClass); return platform; }

void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) { CFStringRef symbols_path = copy_device_support_path(device, CFSTR("Symbols")); CFMutableStringRef cmds = CFStringCreateMutableCopy(NULL, 0, LLDB_PREP_CMDS); CFRange range = { 0, CFStringGetLength(cmds) };

CFStringFindAndReplace(cmds, CFSTR("{platform}"), get_device_platform(device), range, 0);
range.length = CFStringGetLength(cmds);

CFStringFindAndReplace(cmds, CFSTR("{symbols_path}"), symbols_path, range, 0);
range.length = CFStringGetLength(cmds);

CFMutableStringRef pmodule = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)LLDB_FRUITSTRAP_MODULE);

CFRange rangeLLDB = { 0, CFStringGetLength(pmodule) };

CFStringRef exitcode_app_crash_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), exitcode_app_crash);
CFStringFindAndReplace(pmodule, CFSTR("{exitcode_app_crash}"), exitcode_app_crash_str, rangeLLDB, 0);
CFRelease(exitcode_app_crash_str);
rangeLLDB.length = CFStringGetLength(pmodule);

CFStringRef detect_deadlock_timeout_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), _detectDeadlockTimeout);
CFStringFindAndReplace(pmodule, CFSTR("{detect_deadlock_timeout}"), detect_deadlock_timeout_str, rangeLLDB, 0);
CFRelease(detect_deadlock_timeout_str);
rangeLLDB.length = CFStringGetLength(pmodule);

if (args) {
    CFStringRef cf_args = CFStringCreateWithCString(NULL, args, kCFStringEncodingUTF8);
    CFStringFindAndReplace(cmds, CFSTR("{args}"), cf_args, range, 0);
    rangeLLDB.length = CFStringGetLength(pmodule);
    CFStringFindAndReplace(pmodule, CFSTR("{args}"), cf_args, rangeLLDB, 0);

    //printf("write_lldb_prep_cmds:args: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
    //    CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
    CFRelease(cf_args);
} else {
    CFStringFindAndReplace(cmds, CFSTR("{args}"), CFSTR(""), range, 0);
    CFStringFindAndReplace(pmodule, CFSTR("{args}"), CFSTR(""), rangeLLDB, 0);
    //printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
    //    CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
}

if (envs) {
    CFStringRef cf_envs = CFStringCreateWithCString(NULL, envs, kCFStringEncodingUTF8);
    CFStringFindAndReplace(cmds, CFSTR("{envs}"), cf_envs, range, 0);
    rangeLLDB.length = CFStringGetLength(pmodule);
    CFStringFindAndReplace(pmodule, CFSTR("{envs}"), cf_envs, rangeLLDB, 0);

    //printf("write_lldb_prep_cmds:envs: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
    //    CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
    CFRelease(cf_envs);
} else {
    CFStringFindAndReplace(cmds, CFSTR("{envs}"), CFSTR(""), range, 0);
    CFStringFindAndReplace(pmodule, CFSTR("{envs}"), CFSTR(""), rangeLLDB, 0);
    //printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
    //    CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
}
range.length = CFStringGetLength(cmds);

CFStringRef bundle_identifier = copy_disk_app_identifier(disk_app_url);
CFURLRef device_app_url = copy_device_app_url(device, bundle_identifier);
CFRelease(bundle_identifier);
CFStringRef device_app_path = CFURLCopyFileSystemPath(device_app_url, kCFURLPOSIXPathStyle);
CFStringFindAndReplace(cmds, CFSTR("{device_app}"), device_app_path, range, 0);
CFRelease(device_app_path);
range.length = CFStringGetLength(cmds);

CFStringRef disk_app_path = CFURLCopyFileSystemPath(disk_app_url, kCFURLPOSIXPathStyle);
CFStringFindAndReplace(cmds, CFSTR("{disk_app}"), disk_app_path, range, 0);
CFRelease(disk_app_path);
range.length = CFStringGetLength(cmds);

CFStringRef device_port = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), port);
CFStringFindAndReplace(cmds, CFSTR("{device_port}"), device_port, range, 0);
CFRelease(device_port);
range.length = CFStringGetLength(cmds);

if (output_path) {
    CFStringRef output_path_str = CFStringCreateWithCString(NULL, output_path, kCFStringEncodingUTF8);
    CFStringFindAndReplace(cmds, CFSTR("{output_path}"), output_path_str, range, 0);
    CFRelease(output_path_str);
} else {
    CFStringFindAndReplace(cmds, CFSTR("{output_path}"), CFSTR(""), range, 0);
}
range.length = CFStringGetLength(cmds);
if (error_path) {
    CFStringRef error_path_str = CFStringCreateWithCString(NULL, error_path, kCFStringEncodingUTF8);
    CFStringFindAndReplace(cmds, CFSTR("{error_path}"), error_path_str, range, 0);
    CFRelease(error_path_str);
} else {
    CFStringFindAndReplace(cmds, CFSTR("{error_path}"), CFSTR(""), range, 0);
}
range.length = CFStringGetLength(cmds);

CFURLRef device_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, device_app_url);
CFRelease(device_app_url);
CFStringRef device_container_path = CFURLCopyFileSystemPath(device_container_url, kCFURLPOSIXPathStyle);
CFRelease(device_container_url);
CFMutableStringRef dcp_noprivate = CFStringCreateMutableCopy(NULL, 0, device_container_path);
range.length = CFStringGetLength(dcp_noprivate);
CFStringFindAndReplace(dcp_noprivate, CFSTR("/private/var/"), CFSTR("/var/"), range, 0);
range.length = CFStringGetLength(cmds);
CFStringFindAndReplace(cmds, CFSTR("{device_container}"), dcp_noprivate, range, 0);
range.length = CFStringGetLength(cmds);

CFURLRef disk_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, disk_app_url);
CFStringRef disk_container_path = CFURLCopyFileSystemPath(disk_container_url, kCFURLPOSIXPathStyle);
CFRelease(disk_container_url);
CFStringFindAndReplace(cmds, CFSTR("{disk_container}"), disk_container_path, range, 0);
range.length = CFStringGetLength(cmds);

CFStringRef search_paths_pairs = copy_modules_search_paths_pairs(symbols_path, disk_container_path, device_container_path, dcp_noprivate);
CFRelease(symbols_path);
CFRelease(device_container_path);
CFRelease(dcp_noprivate);
CFRelease(disk_container_path);
CFStringFindAndReplace(cmds, CFSTR("{modules_search_paths_pairs}"), search_paths_pairs, range, 0);
range.length = CFStringGetLength(cmds);
CFRelease(search_paths_pairs);

NSString* python_file_path = [NSString stringWithFormat:@"/tmp/%@/fruitstrap_", tmpUUID];
mkdirp(python_file_path);

NSString* python_command = @"fruitstrap_";
if(device_id != NULL) {
    python_file_path = [python_file_path stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
    python_command = [python_command stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
}
python_file_path = [python_file_path stringByAppendingString:@".py"];

CFStringFindAndReplace(cmds, CFSTR("{python_command}"), (CFStringRef)python_command, range, 0);
range.length = CFStringGetLength(cmds);
CFStringFindAndReplace(cmds, CFSTR("{python_file_path}"), (CFStringRef)python_file_path, range, 0);
range.length = CFStringGetLength(cmds);

CFDataRef cmds_data = CFStringCreateExternalRepresentation(NULL, cmds, kCFStringEncodingUTF8, 0);
NSString* prep_cmds_path = [NSString stringWithFormat:PREP_CMDS_PATH, tmpUUID];
if(device_id != NULL) {
    prep_cmds_path = [prep_cmds_path stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
}
FILE *out = fopen([prep_cmds_path UTF8String], "w");
fwrite(CFDataGetBytePtr(cmds_data), CFDataGetLength(cmds_data), 1, out);
CFRelease(cmds_data);
// Write additional commands based on mode we're running in
const char* extra_cmds;
if (!interactive)
{
    if (justlaunch)
      extra_cmds = lldb_prep_noninteractive_justlaunch_cmds;
    else
      extra_cmds = lldb_prep_noninteractive_cmds;
}
else if (nostart)
    extra_cmds = lldb_prep_no_cmds;
else
    extra_cmds = lldb_prep_interactive_cmds;
fwrite(extra_cmds, strlen(extra_cmds), 1, out);
if (custom_commands != nil)
{
    const char * cmds = [custom_commands UTF8String];
    fwrite(cmds, 1, strlen(cmds), out);
}
fclose(out);

out = fopen([python_file_path UTF8String], "w");
CFDataRef pmodule_data = CFStringCreateExternalRepresentation(NULL, pmodule, kCFStringEncodingUTF8, 0);
fwrite(CFDataGetBytePtr(pmodule_data), CFDataGetLength(pmodule_data), 1, out);
CFRelease(pmodule_data);

if (custom_script_path)
{
    FILE * fh = fopen(custom_script_path, "r");
    if (fh == NULL)
    {
        on_error(@"Failed to open %s", custom_script_path);
    }
    fwrite("\n", 1, 1, out);
    char buffer[0x1000];
    size_t bytesRead;
    while ((bytesRead = fread(buffer, 1, sizeof(buffer), fh)) > 0)
    {
        fwrite(buffer, 1, bytesRead, out);
    }
    fclose(fh);
}

fclose(out);

CFRelease(cmds);
CFRelease(pmodule);

}

int kill_ptree(pid_t root, int signum);

const CFStringRef kDbgConnectionPropertyServiceConnection = CFSTR("service_connection"); const CFStringRef kDbgConnectionPropertyLLDBSocket = CFSTR("lldb_socket"); const CFStringRef kDbgConnectionPropertyLLDBSocketRunLoop = CFSTR("lldb_socket_runloop"); const CFStringRef kDbgConnectionPropertyServerSocket = CFSTR("server_socket"); const CFStringRef kDbgConnectionPropertyServerSocketRunLoop = CFSTR("server_socket_runloop");

CFSocketContext get_socket_context(CFNumberRef connection_id) { CFSocketContext context = { 0, (void*)connection_id, NULL, NULL, NULL }; return context; }

CFMutableDictionaryRef get_connection_properties(CFNumberRef connection_id) { // This is no-op if the key already exists CFDictionaryAddValue(debugserver_active_connections, connection_id, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));

return (CFMutableDictionaryRef)CFDictionaryGetValue(debugserver_active_connections, connection_id);

}

void close_connection(CFNumberRef connection_id) { CFMutableDictionaryRef connection_properties = get_connection_properties(connection_id);

ServiceConnRef dbgServiceConnection = (ServiceConnRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyServiceConnection);
AMDServiceConnectionInvalidate(dbgServiceConnection);
CFRelease(dbgServiceConnection);

CFSocketRef lldb_socket = (CFSocketRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyLLDBSocket);
CFSocketInvalidate(lldb_socket);
CFRelease(lldb_socket);

CFRunLoopSourceRef lldb_socket_runloop = (CFRunLoopSourceRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyLLDBSocketRunLoop);
CFRunLoopRemoveSource(CFRunLoopGetMain(), lldb_socket_runloop, kCFRunLoopCommonModes);
CFRelease(lldb_socket_runloop);

CFSocketRef server_socket = (CFSocketRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyServerSocket);
CFSocketInvalidate(server_socket);
CFRelease(server_socket);

CFRunLoopSourceRef server_socket_runloop = (CFRunLoopSourceRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyServerSocketRunLoop);
CFRunLoopRemoveSource(CFRunLoopGetMain(), server_socket_runloop, kCFRunLoopCommonModes);
CFRelease(server_socket_runloop);

CFDictionaryRemoveValue(debugserver_active_connections, connection_id);

}

void server_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void data, void info) { CFNumberRef connection_id = (CFNumberRef)info;

CFMutableDictionaryRef connection_properties = get_connection_properties(connection_id);

ServiceConnRef dbgServiceConnection = (ServiceConnRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyServiceConnection);
CFSocketRef lldb_socket = (CFSocketRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyLLDBSocket);

char buffer[0x1000];
int bytesRead;
do {
    bytesRead = AMDServiceConnectionReceive(dbgServiceConnection, buffer, sizeof(buffer));
    if (bytesRead == 0)
    {
        // close the socket on which we've got end-of-file, the server_socket.
        close_connection(connection_id);
        return;
    }
    write(CFSocketGetNative(lldb_socket), buffer, bytesRead);
}
while (bytesRead == sizeof(buffer));

}

void lldb_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void data, void info) { CFNumberRef connection_id = (CFNumberRef)info;

CFMutableDictionaryRef connection_properties = get_connection_properties(connection_id);

ServiceConnRef dbgServiceConnection = (ServiceConnRef)CFDictionaryGetValue(connection_properties, kDbgConnectionPropertyServiceConnection);

if (CFDataGetLength(data) == 0) {
    // close the socket on which we've got end-of-file, the lldb_socket.
    close_connection(connection_id);
    return;
}
int __unused sent = AMDServiceConnectionSend(dbgServiceConnection, CFDataGetBytePtr(data),  CFDataGetLength (data));
assert (CFDataGetLength (data) == sent);

}

ServiceConnRef start_remote_debug_server(AMDeviceRef device) { ServiceConnRef dbgServiceConnection = NULL; CFStringRef serviceName = CFSTR("com.apple.debugserver"); CFStringRef keys[] = { CFSTR("MinIPhoneVersion"), CFSTR("MinAppleTVVersion"), CFSTR("MinWatchVersion") }; CFStringRef values[] = { CFSTR("14.0"), CFSTR("14.0"), CFSTR("7.0") }; // Not sure about older watchOS versions CFDictionaryRef version = CFDictionaryCreate(NULL, (const void )&keys, (const void )&values, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

bool useSecureProxy = AMDeviceIsAtLeastVersionOnPlatform(device, version);
if (useSecureProxy)
{
    serviceName = CFSTR("com.apple.debugserver.DVTSecureSocketProxy");
}

int start_err = AMDeviceSecureStartService(device, serviceName, NULL, &dbgServiceConnection);
if (start_err != 0)
{
    // After we mount the image, iOS needs to scan the image to register new services.
    // If we ask to start the service before it is found by ios, we will get 0xe8000022.
    // In other cases, it's been observed, that device may loose connection here (0xe800002d).
    // Luckly, we can just restart the connection and continue.
    // In other cases we just error out.
    NSLogOut(@"Failed to start debugserver: %x %s", start_err, get_error_message(start_err));
    switch(start_err)
    {
        case 0xe8000022:
            NSLogOut(@"Waiting for the device to scan mounted image");
            sleep(1);
            break;
        case 0x800002d:
            NSLogOut(@"Reconnecting to device");
            // We dont call AMDeviceStopSession as we cannot send any messages anymore
            check_error(AMDeviceDisconnect(device));
            connect_and_start_session(device);
            break;
        default:
            check_error(start_err);
    }
    check_error(AMDeviceSecureStartService(device, serviceName, NULL, &dbgServiceConnection));
}
assert(dbgServiceConnection != NULL);

if (!useSecureProxy)
{
    disable_ssl(dbgServiceConnection);
}

return dbgServiceConnection;

}

void create_remote_debug_server_socket(CFNumberRef connection_id, AMDeviceRef device) { CFMutableDictionaryRef connection_properties = get_connection_properties(connection_id);

ServiceConnRef dbgServiceConnection = start_remote_debug_server(device);
CFDictionaryAddValue(connection_properties, kDbgConnectionPropertyServiceConnection, dbgServiceConnection);

/*
 * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate
 * socket to transfer data.
 */
CFSocketContext context = get_socket_context(connection_id);
CFSocketRef server_socket = CFSocketCreateWithNative (NULL, AMDServiceConnectionGetSocket(dbgServiceConnection), kCFSocketReadCallBack, &server_callback, &context);
CFDictionaryAddValue(connection_properties, kDbgConnectionPropertyServerSocket, server_socket);

CFRunLoopSourceRef server_socket_runloop = CFSocketCreateRunLoopSource(NULL, server_socket, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), server_socket_runloop, kCFRunLoopCommonModes);
CFDictionaryAddValue(connection_properties, kDbgConnectionPropertyServerSocketRunLoop, server_socket_runloop);

}

void create_local_lldb_socket(CFNumberRef connection_id, CFSocketNativeHandle socket) { CFMutableDictionaryRef connection_properties = get_connection_properties(connection_id);

CFSocketContext context = get_socket_context(connection_id);
CFSocketRef lldb_socket  = CFSocketCreateWithNative(NULL, socket, kCFSocketDataCallBack, &lldb_callback, &context);
CFDictionaryAddValue(connection_properties, kDbgConnectionPropertyLLDBSocket, lldb_socket);

int flag = 1;
int res = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
if (res == -1) {
  on_sys_error(@"Setting socket option failed.");
}
CFRunLoopSourceRef lldb_socket_runloop = CFSocketCreateRunLoopSource(NULL, lldb_socket, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), lldb_socket_runloop, kCFRunLoopCommonModes);
CFDictionaryAddValue(connection_properties, kDbgConnectionPropertyLLDBSocketRunLoop, lldb_socket_runloop);

}

void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void data, void info) { static int next_connection_id = 0; CFNumberRef connection_id = CFAutorelease(CFNumberCreate(NULL, kCFNumberIntType, &next_connection_id));

assert (callbackType == kCFSocketAcceptCallBack);

if (debugserver_only) {
    // In case of server mode, we start the debug server connection every time we accept a connection
    create_remote_debug_server_socket(connection_id, (AMDeviceRef)info);
    ++next_connection_id;
}

CFSocketNativeHandle socket = (CFSocketNativeHandle)(*((CFSocketNativeHandle *)data));
create_local_lldb_socket(connection_id, socket);

if (!debugserver_only) {
    // Stop listening after first connection in case not in server mode
    CFSocketInvalidate(s);
    CFRelease(s);
}

}

void start_debug_server_multiplexer(AMDeviceRef device) { debugserver_active_connections = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

CFSocketContext context = { 0, device, NULL, NULL, NULL };
CFSocketRef fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, &context);

if (port) {
    int yes = 1;
    setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
}

CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&addr4, sizeof(addr4));

CFSocketSetAddress(fdvendor, address_data);
CFRelease(address_data);
socklen_t addrlen = sizeof(addr4);
int res = getsockname(CFSocketGetNative(fdvendor),(struct sockaddr *)&addr4,&addrlen);
if (res == -1) {
  on_sys_error(@"Getting socket name failed.");
}
port = ntohs(addr4.sin_port);

if (fdvendor_runloop) {
    CFRelease(fdvendor_runloop);
}
fdvendor_runloop = CFSocketCreateRunLoopSource(NULL, fdvendor, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), fdvendor_runloop, kCFRunLoopCommonModes);

}

void kill_ptree_inner(pid_t root, int signum, struct kinfo_proc *kp, int kp_len) { int i; for (i = 0; i < kp_len; i++) { if (kp[i].kp_eproc.e_ppid == root) { kill_ptree_inner(kp[i].kp_proc.p_pid, signum, kp, kp_len); } } if (root != getpid()) { kill(root, signum); } }

int kill_ptree(pid_t root, int signum) { int mib[3]; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_ALL; if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1) { return -1; }

struct kinfo_proc *kp = calloc(1, len);
if (!kp) {
    return -1;
}

if (sysctl(mib, 3, kp, &len, NULL, 0) == -1) {
    free(kp);
    return -1;
}

kill_ptree_inner(root, signum, kp, (int)(len / sizeof(struct kinfo_proc)));

free(kp);
return 0;

}

void killed(int signum) { // SIGKILL needed to kill lldb, probably a better way to do this. kill(0, SIGKILL); _exit(0); }

void lldb_finished_handler(int signum) { int status = 0; if (waitpid(child, &status, 0) == -1) perror("waitpid failed"); _exit(WEXITSTATUS(status)); }

pid_t bring_process_to_foreground(void) { pid_t fgpid = tcgetpgrp(STDIN_FILENO); if (setpgid(0, 0) == -1) perror("setpgid failed");

signal(SIGTTOU, SIG_IGN);
if (tcsetpgrp(STDIN_FILENO, getpid()) == -1)
    perror("tcsetpgrp failed");
signal(SIGTTOU, SIG_DFL);
return fgpid;

}

void setup_dummy_pipe_on_stdin(int pfd[2]) { if (pipe(pfd) == -1) perror("pipe failed"); if (dup2(pfd[0], STDIN_FILENO) == -1) perror("dup2 failed"); }

void setup_lldb(AMDeviceRef device, CFURLRef url) { CFStringRef device_full_name = get_device_full_name(device), device_interface_name = get_device_interface_name(device);

connect_and_start_session(device);
CFBooleanRef is_password_protected = AMDeviceCopyValue(device, 0, CFSTR("PasswordProtected"));
NSLogJSON(@{@"Event": @"PasswordProtectedStatus",
            @"Status": @(CFBooleanGetValue(is_password_protected)),
});
CFRelease(is_password_protected);

NSLogOut(@"------ Debug phase ------");

NSLogOut(@"Starting debug of %@ connected through %@...", device_full_name, device_interface_name);

mount_developer_image(device);      // put debugserver on the device

start_debug_server_multiplexer(device);  // start debugserver proxy listener
if (debugserver_only) {
    NSLogOut(@"[100%%] Listening for lldb connections");
}
else {
    int connection_id = 0;
    CFNumberRef cf_connection_id = CFAutorelease(CFNumberCreate(NULL, kCFNumberIntType, &connection_id));

    create_remote_debug_server_socket(cf_connection_id, device);   // start debugserver
    write_lldb_prep_cmds(device, url);   // dump the necessary lldb commands into a file
    NSLogOut(@"[100%%] Connecting to remote debug server");
}
NSLogOut(@"-------------------------");

if (url != NULL)
    CFRelease(url);

setpgid(getpid(), 0);
signal(SIGHUP, killed);
signal(SIGINT, killed);
signal(SIGTERM, killed);
// Need this before fork to avoid race conditions. For child process we remove this right after fork.
signal(SIGLLDB, lldb_finished_handler);

parent = getpid();

}

void launch_debugger(AMDeviceRef device, CFURLRef url) { setup_lldb(device, url); int pid = fork(); if (pid == 0) { signal(SIGHUP, SIG_DFL); signal(SIGLLDB, SIG_DFL); child = getpid(); pid_t oldfgpid = 0; int pfd[2] = {-1, -1}; if (isatty(STDIN_FILENO)) // If we are running on a terminal, then we need to bring process to foreground for input // to work correctly on lldb's end. oldfgpid = bring_process_to_foreground(); else // If lldb is running in a non terminal environment, then it freaks out spamming "^D" and // "quit". It seems this is caused by read() on stdin returning EOF in lldb. To hack around // this we setup a dummy pipe on stdin, so read() would block expecting "user's" input. setup_dummy_pipe_on_stdin(pfd);

    NSString* lldb_shell;
    NSString* prep_cmds = [NSString stringWithFormat:PREP_CMDS_PATH, tmpUUID];
    lldb_shell = [NSString stringWithFormat:LLDB_SHELL, prep_cmds];

    if(device_id != NULL) {
        lldb_shell = [lldb_shell stringByAppendingString: [[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
    }

    int status = system([lldb_shell UTF8String]); // launch lldb
    if (status == -1)
        perror("failed launching lldb");

    close(pfd[0]);
    close(pfd[1]);

    // Notify parent we're exiting
    kill(parent, SIGLLDB);

    if (oldfgpid) {
        tcsetpgrp(STDIN_FILENO, oldfgpid);
    }
    // Pass lldb exit code
    _exit(WEXITSTATUS(status));
} else if (pid > 0) {
    child = pid;
} else {
    on_sys_error(@"Fork failed");
}

}

void launch_debugger_and_exit(AMDeviceRef device, CFURLRef url) { setup_lldb(device,url); int pfd[2] = {-1, -1}; if (pipe(pfd) == -1) perror("Pipe failed"); int pid = fork(); if (pid == 0) { signal(SIGHUP, SIG_DFL); signal(SIGLLDB, SIG_DFL); child = getpid();

    if (dup2(pfd[0],STDIN_FILENO) == -1)
        perror("dup2 failed");

    NSString* prep_cmds = [NSString stringWithFormat:PREP_CMDS_PATH, tmpUUID];
    NSString* lldb_shell = [NSString stringWithFormat:LLDB_SHELL, prep_cmds];
    if(device_id != NULL) {
        lldb_shell = [lldb_shell stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
    }

    int status = system([lldb_shell UTF8String]); // launch lldb
    if (status == -1)
        perror("failed launching lldb");

    close(pfd[0]);

    // Notify parent we're exiting
    kill(parent, SIGLLDB);
    // Pass lldb exit code
    _exit(WEXITSTATUS(status));
} else if (pid > 0) {
    child = pid;
    NSLogVerbose(@"Waiting for child [Child: %d][Parent: %d]\n", child, parent);
} else {
    on_sys_error(@"Fork failed");
}

}

void launch_debugserver_only(AMDeviceRef device, CFURLRef url) { if (url != NULL) CFRetain(url); setup_lldb(device,url);

CFStringRef device_app_path = NULL;
if (url != NULL) {
  CFStringRef bundle_identifier = copy_disk_app_identifier(url);
  CFURLRef device_app_url = copy_device_app_url(device, bundle_identifier);
  CFRelease(bundle_identifier);
  device_app_path = CFURLCopyFileSystemPath(device_app_url, kCFURLPOSIXPathStyle);
  CFRelease(device_app_url);
  CFRelease(url);
}

NSLogOut(@"debugserver port: %d", port);
if (device_app_path == NULL) {
    NSLogJSON(@{@"Event": @"DebugServerLaunched",
                @"Port": @(port),
                });
} else {
    NSLogOut(@"App path: %@", device_app_path);
    NSLogJSON(@{@"Event": @"DebugServerLaunched",
                @"Port": @(port),
                @"Path": (__bridge NSString *)device_app_path
                });
    CFRelease(device_app_path);
}

}

CFStringRef copy_bundle_id(CFURLRef app_url) { if (app_url == NULL) return NULL;

CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, app_url, CFSTR("Info.plist"), false);

if (url == NULL)
    return NULL;

CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, url);
CFRelease(url);

if (stream == NULL)
    return NULL;

CFPropertyListRef plist = NULL;
if (CFReadStreamOpen(stream) == TRUE) {
    plist = CFPropertyListCreateWithStream(NULL, stream, 0,
                                           kCFPropertyListImmutable, NULL, NULL);
}
CFReadStreamClose(stream);
CFRelease(stream);

if (plist == NULL)
    return NULL;

const void *value = CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier"));
CFStringRef bundle_id = NULL;
if (value != NULL)
    bundle_id = CFRetain(value);

CFRelease(plist);
return bundle_id;

}

typedef enum { READ_DIR_FILE, READ_DIR_FIFO, READ_DIR_BEFORE_DIR, READ_DIR_AFTER_DIR } read_dir_cb_reason;

void read_dir(AFCConnectionRef afc_conn_p, const char dir, void(callback)(AFCConnectionRef conn, const char dir, read_dir_cb_reason reason)) { char dir_ent;

afc_dictionary* afc_dict_p;
char *key, *val;
int not_dir = 0;
bool is_fifo = 0;

unsigned int code = AFCFileInfoOpen(afc_conn_p, dir, &afc_dict_p);
if (code != 0) {
    // there was a problem reading or opening the file to get info on it, abort
    return;
}

long long mtime = -1;
long long birthtime = -1;
long size = -1;
long blocks = -1;
long nlink = -1;
NSString * ifmt = nil;
while((AFCKeyValueRead(afc_dict_p,&key,&val) == 0) && key && val) {
    if (strcmp(key,"st_ifmt")==0) {
        not_dir = strcmp(val,"S_IFDIR");
        is_fifo = !strcmp(val, "S_IFIFO");
        if (_json_output) {
            ifmt = [NSString stringWithUTF8String:val];
        } else {
            break;
        }
    } else if (strcmp(key, "st_size") == 0) {
        size = atol(val);
    } else if (strcmp(key, "st_mtime") == 0) {
        mtime = atoll(val);
    } else if (strcmp(key, "st_birthtime") == 0) {
        birthtime = atoll(val);
    } else if (strcmp(key, "st_nlink") == 0) {
        nlink = atol(val);
    } else if (strcmp(key, "st_blocks") == 0) {
        blocks = atol(val);
    }
}
AFCKeyValueClose(afc_dict_p);

if (_json_output) {
    if (_file_meta_info == nil) {
        _file_meta_info = [[NSMutableArray alloc] init];
    }
    [_file_meta_info addObject: @{@"full_path": [NSString stringWithUTF8String:dir],
                                  @"st_ifmt": ifmt,
                                  @"st_nlink": @(nlink),
                                  @"st_size": @(size),
                                  @"st_blocks": @(blocks),
                                  @"st_mtime": @(mtime),
                                  @"st_birthtime": @(birthtime)}];
}

if (not_dir) {
    if (callback) (*callback)(afc_conn_p, dir, is_fifo ? READ_DIR_FIFO : READ_DIR_FILE);
    return;
}

afc_directory* afc_dir_p;
afc_error_t err = AFCDirectoryOpen(afc_conn_p, dir, &afc_dir_p);

if (err != 0) {
    // Couldn't open dir - was probably a file
    return;
}

// Call the callback on the directory before processing its
// contents. This is used by copy file callback, which needs to
// create the directory on the host before attempting to copy
// files into it.
if (callback) (*callback)(afc_conn_p, dir, READ_DIR_BEFORE_DIR);

while(true) {
    err = AFCDirectoryRead(afc_conn_p, afc_dir_p, &dir_ent);

    if (err != 0 || !dir_ent)
        break;

    if (strcmp(dir_ent, ".") == 0 || strcmp(dir_ent, "..") == 0)
        continue;

    char* dir_joined = malloc(strlen(dir) + strlen(dir_ent) + 2);
    strcpy(dir_joined, dir);
    if (dir_joined[strlen(dir)-1] != '/')
        strcat(dir_joined, "/");
    strcat(dir_joined, dir_ent);
    if (!(non_recursively && strcmp(list_root, dir) != 0)) {
        read_dir(afc_conn_p, dir_joined, callback);
    }
    free(dir_joined);
}

AFCDirectoryClose(afc_conn_p, afc_dir_p);

// Call the callback on the directory after processing its
// contents. This is used by the rmtree callback because it needs
// to delete the directory's contents before the directory itself
if (callback) (*callback)(afc_conn_p, dir, READ_DIR_AFTER_DIR);

}

AFCConnectionRef start_afc_service(AMDeviceRef device) { AMDeviceConnect(device); assert(AMDeviceIsPaired(device)); check_error(AMDeviceValidatePairing(device)); check_error(AMDeviceStartSession(device));

AFCConnectionRef conn = NULL;
ServiceConnRef serviceConn = NULL;

if (AMDeviceStartService(device, AMSVC_AFC, &serviceConn, 0) != MDERR_OK) {
    on_error(@"Unable to start file service!");
}
if (AFCConnectionOpen(serviceConn, 0, &conn) != MDERR_OK) {
    on_error(@"Unable to open connection!");
}

check_error(AMDeviceStopSession(device));
check_error(AMDeviceDisconnect(device));
return conn;

}

// Used to send files to app-specific sandbox (Documents dir) AFCConnectionRef start_house_arrest_service(AMDeviceRef device) { connect_and_start_session(device);

AFCConnectionRef conn = NULL;

if (bundle_id == NULL) {
    on_error(@"Bundle id is not specified");
}

CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
CFStringRef keys[1];
keys[0] = CFSTR("Command");
CFStringRef values[1];
values[0] = CFSTR("VendDocuments");
CFDictionaryRef command = CFDictionaryCreate(kCFAllocatorDefault,
                                             (void*)keys,
                                             (void*)values,
                                             1,
                                             &kCFTypeDictionaryKeyCallBacks,
                                             &kCFTypeDictionaryValueCallBacks);
if (AMDeviceCreateHouseArrestService(device, cf_bundle_id, 0, &conn) != 0 &&
    AMDeviceCreateHouseArrestService(device, cf_bundle_id, command, &conn) != 0) {
    on_error(@"Unable to find bundle with id: %@", [NSString stringWithUTF8String:bundle_id]);
}

check_error(AMDeviceStopSession(device));
check_error(AMDeviceDisconnect(device));
CFRelease(cf_bundle_id);

return conn;

}

// Uses realpath() to resolve any symlinks in a path. Returns the resolved // path or the original path if an error occurs. This allocates memory for the // resolved path and the caller is responsible for freeing it. char resolve_path(char path) { char buffer[PATH_MAX]; // Use the original path if realpath() fails, otherwise use resolved value. char resolved_path = realpath(path, buffer) == NULL ? path : buffer; char new_path = malloc(strlen(resolved_path) + 1); strcpy(new_path, resolved_path); return new_path; }

char const get_filename_from_path(char const path) { char constptr = path + strlen(path); while (ptr > path) { if (ptr == '/') break; --ptr; } if (ptr+1 >= path+strlen(path)) return NULL; if (ptr == path) return ptr; return ptr+1; }

void read_file_to_memory(char const path, size_t* file_size) { struct stat buf; int err = stat(path, &buf); if (err < 0) { return NULL; }

*file_size = buf.st_size;
FILE* fd = fopen(path, "r");
char* content = malloc(*file_size);
if (*file_size != 0 && fread(content, *file_size, 1, fd) != 1)
{
    fclose(fd);
    return NULL;
}
fclose(fd);
return content;

}

void list_files_callback(AFCConnectionRef conn, const char *name, read_dir_cb_reason reason) { if (reason == READ_DIR_FILE || reason == READ_DIR_FIFO) { NSLogOut(@"%@", [NSString stringWithUTF8String:name]); } else if (reason == READ_DIR_BEFORE_DIR) { NSLogOut(@"%@/", [NSString stringWithUTF8String:name]); } }

void list_files(AMDeviceRef device) { AFCConnectionRef afc_conn_p; if (file_system) { afc_conn_p = start_afc_service(device); } else { afc_conn_p = start_house_arrest_service(device); } assert(afc_conn_p); if (_json_output) { read_dir(afc_conn_p, list_root?list_root:"/", NULL); NSLogJSON(@{@"Event": @"FileListed", @"Files": _file_meta_info}); } else { read_dir(afc_conn_p, list_root?list_root:"/", list_files_callback); }

check_error(AFCConnectionClose(afc_conn_p));

}

int app_exists(AMDeviceRef device) { if (bundle_id == NULL) { NSLogOut(@"Bundle id is not specified."); return 1; } AMDeviceConnect(device); assert(AMDeviceIsPaired(device)); check_error(AMDeviceValidatePairing(device)); check_error(AMDeviceStartSession(device));

CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);

NSArray *a = [NSArray arrayWithObjects:@"CFBundleIdentifier", nil];
NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
CFDictionaryRef options = (CFDictionaryRef)optionsDict;
CFDictionaryRef result = nil;
check_error(AMDeviceLookupApplications(device, options, &result));

bool appExists = CFDictionaryContainsKey(result, cf_bundle_id);
NSLogOut(@"%@", appExists ? @"true" : @"false");
CFRelease(cf_bundle_id);

check_error(AMDeviceStopSession(device));
check_error(AMDeviceDisconnect(device));
if (appExists)
    return 0;
return -1;

}

void get_battery_level(AMDeviceRef device) {

AMDeviceConnect(device);
assert(AMDeviceIsPaired(device));
check_error(AMDeviceValidatePairing(device));
check_error(AMDeviceStartSession(device));

CFStringRef result = AMDeviceCopyValue(device, (void*)@"com.apple.mobile.battery", (__bridge CFStringRef)@"BatteryCurrentCapacity");
NSLogOut(@"BatteryCurrentCapacity:%@",result);
CFRelease(result);

check_error(AMDeviceStopSession(device));
check_error(AMDeviceDisconnect(device));

}

void replace_dict_date_with_absolute_time(CFMutableDictionaryRef dict, CFStringRef key) { CFDateRef date = CFDictionaryGetValue(dict, key); CFAbsoluteTime absolute_date = CFDateGetAbsoluteTime(date); CFNumberRef absolute_date_ref = CFNumberCreate(NULL, kCFNumberDoubleType, &absolute_date); CFDictionaryReplaceValue(dict, key, absolute_date_ref); CFRelease(absolute_date_ref); }

void list_provisioning_profiles(AMDeviceRef device) { connect_and_start_session(device); CFArrayRef device_provisioning_profiles = AMDeviceCopyProvisioningProfiles(device);

CFIndex provisioning_profiles_count = CFArrayGetCount(device_provisioning_profiles);
CFMutableArrayRef serializable_provisioning_profiles =
    CFArrayCreateMutable(NULL, provisioning_profiles_count, &kCFTypeArrayCallBacks);

for (CFIndex i = 0; i < provisioning_profiles_count; i++) {
    void *device_provisioning_profile =
        (void *)CFArrayGetValueAtIndex(device_provisioning_profiles, i);
    CFMutableDictionaryRef serializable_provisioning_profile;

    if (verbose) {
        // Verbose output; We selectively omit keys from profile.
        CFDictionaryRef immutable_profile_dict =
            MISProfileCopyPayload(device_provisioning_profile);
        serializable_provisioning_profile =
            CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, immutable_profile_dict);
        CFRelease(immutable_profile_dict);

        // Remove binary values from the output since they aren't readable and add a whole lot
        // of noise to the output.
        CFDictionaryRemoveValue(serializable_provisioning_profile,
                                CFSTR("DER-Encoded-Profile"));
        CFDictionaryRemoveValue(serializable_provisioning_profile,
                                CFSTR("DeveloperCertificates"));
    } else {
        // Normal output; We selectively include keys from profile.
        CFStringRef keys[] = {CFSTR("Name"), CFSTR("UUID"), CFSTR("ExpirationDate")};
        CFIndex size = sizeof(keys) / sizeof(CFStringRef);
        serializable_provisioning_profile =
            CFDictionaryCreateMutable(kCFAllocatorDefault, size, &kCFTypeDictionaryKeyCallBacks,
                                      &kCFTypeDictionaryValueCallBacks);
        for (CFIndex i = 0; i < size; i++) {
            CFStringRef key = keys[i];
            CFStringRef value = MISProfileGetValue(device_provisioning_profile, key);
            CFDictionaryAddValue(serializable_provisioning_profile, key, value);
        }
    }

    if (_json_output) {
        // JSON output can't have CFDate objects so convert dates into CFAbsoluteTime's.
        replace_dict_date_with_absolute_time(serializable_provisioning_profile,
                                             CFSTR("ExpirationDate"));
        replace_dict_date_with_absolute_time(serializable_provisioning_profile,
                                             CFSTR("CreationDate"));
    }

    CFArrayAppendValue(serializable_provisioning_profiles, serializable_provisioning_profile);
    CFRelease(serializable_provisioning_profile);
}
CFRelease(device_provisioning_profiles);

if (_json_output) {
    NSLogJSON(@{
        @"Event" : @"ListProvisioningProfiles",
        @"Profiles" : (NSArray *)serializable_provisioning_profiles
    });
} else {
    NSLogOut(@"%@", serializable_provisioning_profiles);
}
CFRelease(serializable_provisioning_profiles);

}

void install_provisioning_profile(AMDeviceRef device) { if (!profile_path) { on_error(@"no path to provisioning profile specified"); }

size_t file_size = 0;
void *file_content = read_file_to_memory(profile_path, &file_size);
CFDataRef profile_data = CFDataCreate(NULL, file_content, file_size);
void *profile = MISProfileCreateWithData(0, profile_data);
connect_and_start_session(device);
check_error(AMDeviceInstallProvisioningProfile(device, profile));

free(file_content);
CFRelease(profile_data);
CFRelease(profile);

}

void uninstall_provisioning_profile(AMDeviceRef device) { if (!profile_uuid) { on_error(@"no profile UUID specified via --profile-uuid"); }

CFStringRef uuid = CFStringCreateWithCString(NULL, profile_uuid, kCFStringEncodingUTF8);
connect_and_start_session(device);
check_error(AMDeviceRemoveProvisioningProfile(device, uuid));
CFRelease(uuid);

}

void download_provisioning_profile(AMDeviceRef device) { if (!profile_uuid) { on_error(@"no profile UUID specified via --profile-uuid"); } else if (!profile_path) { on_error(@"no download path specified"); }

connect_and_start_session(device);
CFArrayRef device_provisioning_profiles = AMDeviceCopyProvisioningProfiles(device);
CFIndex provisioning_profiles_count = CFArrayGetCount(device_provisioning_profiles);
CFStringRef uuid = CFStringCreateWithCString(NULL, profile_uuid, kCFStringEncodingUTF8);
bool found_matching_uuid = false;

for (CFIndex i = 0; i < provisioning_profiles_count; i++) {
    void *profile = (void *)CFArrayGetValueAtIndex(device_provisioning_profiles, i);
    CFStringRef other_uuid = MISProfileGetValue(profile, CFSTR("UUID"));
    found_matching_uuid = CFStringCompare(uuid, other_uuid, 0) == kCFCompareEqualTo;

    if (found_matching_uuid) {
        NSLogVerbose(@"Writing %@ to %s", MISProfileGetValue(profile, CFSTR("Name")),
                     profile_path);
        CFStringRef dst_path =
            CFStringCreateWithCString(NULL, profile_path, kCFStringEncodingUTF8);
        check_error(MISProfileWriteToFile(profile, dst_path));
        CFRelease(dst_path);
        break;
    }
}

CFRelease(uuid);
CFRelease(device_provisioning_profiles);
if (!found_matching_uuid) {
    on_error(@"Did not find provisioning profile with UUID %x on device", profile_uuid);
}

}

void list_bundle_id(AMDeviceRef device) { connect_and_start_session(device); NSMutableArray a = [NSMutableArray arrayWithObjects: @"CFBundleIdentifier", @"CFBundleName", @"CFBundleDisplayName", @"CFBundleVersion", @"CFBundleShortVersionString", nil]; if (keys) { for (NSString key in keys) { [a addObjectsFromArray:[key componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@",&"]]]; } } NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"]; CFDictionaryRef options = (CFDictionaryRef)optionsDict; CFDictionaryRef result = nil; check_error(AMDeviceLookupApplications(device, options, &result));

if (bundle_id != NULL) {
    CFStringRef cf_bundle_id = CFAutorelease(CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8));
    CFDictionaryRef app_dict = CFRetain(CFDictionaryGetValue(result, cf_bundle_id));

    CFRelease(result);
    result = CFAutorelease(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));

    if (app_dict != NULL) {
        CFDictionaryAddValue((CFMutableDictionaryRef)result, cf_bundle_id, app_dict);
        CFRelease(app_dict);
    }
}

if (_json_output) {
    NSLogJSON(@{@"Event": @"ListBundleId",
                @"Apps": (NSDictionary *)result});
} else {
    CFIndex count;
    count = CFDictionaryGetCount(result);
    const void *keys[count];
    CFDictionaryGetKeysAndValues(result, keys, NULL);
    for(int i = 0; i < count; ++i) {
        NSLogOut(@"%@", (CFStringRef)keys[i]);
    }
}

check_error(AMDeviceStopSession(device));
check_error(AMDeviceDisconnect(device));

}

void copy_file_callback(AFCConnectionRef afc_conn_p, const char name, read_dir_cb_reason reason) { const char local_name=name;

if (*local_name=='/') local_name++;

if (*local_name=='\0') return;

if (reason == READ_DIR_FILE || reason == READ_DIR_FIFO) {
    NSLogOut(@"%@", [NSString stringWithUTF8String:name]);
    afc_file_ref fref;
    int err = AFCFileRefOpen(afc_conn_p,name,1,&fref);

    if (err) {
        fprintf(stderr,"AFCFileRefOpen(\"%s\") failed: %d\n",name,err);
        return;
    }

    FILE *fp = fopen(local_name,"w");

    if (fp==NULL) {
        fprintf(stderr,"fopen(\"%s\",\"w\") failer: %s\n",local_name,strerror(errno));
        AFCFileRefClose(afc_conn_p,fref);
        return;
    }

    char buf[4096];
    size_t sz=sizeof(buf);

    while (AFCFileRefRead(afc_conn_p,fref,buf,&sz)==0 && sz) {
        fwrite(buf,sz,1,fp);
        sz = sizeof(buf);
    }

    AFCFileRefClose(afc_conn_p,fref);
    fclose(fp);

} else if (reason == READ_DIR_BEFORE_DIR) {
    NSLogOut(@"%@/", [NSString stringWithUTF8String:name]);
    if (mkdir(local_name,0777) && errno!=EEXIST) {
        fprintf(stderr,"mkdir(\"%s\") failed: %s\n",local_name,strerror(errno));
    }
}

}

void download_tree(AMDeviceRef device) { AFCConnectionRef afc_conn_p; if (file_system) { afc_conn_p = start_afc_service(device); } else { afc_conn_p = start_house_arrest_service(device); }

assert(afc_conn_p);
char *dirname = NULL;

list_root = list_root? list_root : "/";
target_filename = target_filename? target_filename : ".";

NSString* targetPath = [NSString pathWithComponents:@[ @(target_filename), @(list_root)] ];
mkdirp([targetPath stringByDeletingLastPathComponent]);

do {
    if (target_filename) {
        dirname = strdup(target_filename);
        mkdirp(@(dirname));
        if (mkdir(dirname,0777) && errno!=EEXIST) {
            fprintf(stderr,"mkdir(\"%s\") failed: %s\n",dirname,strerror(errno));
            break;
        }
        if (chdir(dirname)) {
            fprintf(stderr,"chdir(\"%s\") failed: %s\n",dirname,strerror(errno));
            break;
        }
    }
    read_dir(afc_conn_p, list_root, copy_file_callback);
} while(0);

if (dirname) free(dirname);
if (afc_conn_p) AFCConnectionClose(afc_conn_p);

}

void upload_dir(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString source, NSString destination); void upload_single_file(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString sourcePath, NSString destinationPath);

void upload_file(AMDeviceRef device) { AFCConnectionRef afc_conn_p; if (file_system) { afc_conn_p = start_afc_service(device); } else { afc_conn_p = start_house_arrest_service(device); } assert(afc_conn_p);

if (!target_filename)
{
    target_filename = get_filename_from_path(upload_pathname);
}

NSString* sourcePath = [NSString stringWithUTF8String: upload_pathname];
NSString* destinationPath = [NSString stringWithUTF8String: target_filename];

BOOL isDir;
bool exists = [[NSFileManager defaultManager] fileExistsAtPath: sourcePath isDirectory: &isDir];
if (!exists)
{
    on_error(@"Could not find file: %s", upload_pathname);
}
else if (isDir)
{
    upload_dir(device, afc_conn_p, sourcePath, destinationPath);
}
else
{
    upload_single_file(device, afc_conn_p, sourcePath, destinationPath);
}
check_error(AFCConnectionClose(afc_conn_p));

}

void upload_single_file(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString sourcePath, NSString destinationPath) {

afc_file_ref file_ref;

size_t file_size;
void* file_content = read_file_to_memory([sourcePath fileSystemRepresentation], &file_size);

if (!file_content)
{
    on_error(@"Could not open file: %@", sourcePath);
}

// Make sure the directory was created
{
    NSString *dirpath = [destinationPath stringByDeletingLastPathComponent];
    check_error(AFCDirectoryCreate(afc_conn_p, [dirpath fileSystemRepresentation]));
}

NSLogVerbose(@"%@", destinationPath);
NSLogJSON(@{@"Event": @"UploadFile",
            @"Destination": destinationPath
            });

int ret = AFCFileRefOpen(afc_conn_p, [destinationPath fileSystemRepresentation], 3, &file_ref);
if (ret == 0x000a) {
    on_error(@"Cannot write to %@. Permission error.", destinationPath);
}
if (ret == 0x0009) {
    on_error(@"Target %@ is a directory.", destinationPath);
}
assert(ret == 0);
check_error(AFCFileRefWrite(afc_conn_p, file_ref, file_content, file_size));
check_error(AFCFileRefClose(afc_conn_p, file_ref));

free(file_content);

}

void upload_dir(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString source, NSString destination) { check_error(AFCDirectoryCreate(afc_conn_p, [destination fileSystemRepresentation])); for (NSString item in [[NSFileManager defaultManager] contentsOfDirectoryAtPath: source error: nil]) { NSString sourcePath = [source stringByAppendingPathComponent: item]; NSString destinationPath = [destination stringByAppendingPathComponent: item]; BOOL isDir; [[NSFileManager defaultManager] fileExistsAtPath: sourcePath isDirectory: &isDir]; if (isDir) { NSString dirDestinationPath = [destinationPath stringByAppendingString:@"/"]; NSLogVerbose(@"%@", dirDestinationPath); NSLogJSON(@{@"Event": @"UploadDir", @"Destination": dirDestinationPath }); upload_dir(device, afc_conn_p, sourcePath, destinationPath); } else { upload_single_file(device, afc_conn_p, sourcePath, destinationPath); } } }

void make_directory(AMDeviceRef device) { AFCConnectionRef afc_conn_p; if (file_system) { afc_conn_p = start_afc_service(device); } else { afc_conn_p = start_house_arrest_service(device); } assert(afc_conn_p); check_error(AFCDirectoryCreate(afc_conn_p, target_filename)); check_error(AFCConnectionClose(afc_conn_p)); }

void remove_path(AMDeviceRef device) { AFCConnectionRef afc_conn_p; if (file_system) { afc_conn_p = start_afc_service(device); } else { afc_conn_p = start_house_arrest_service(device); } assert(afc_conn_p); check_error(AFCRemovePath(afc_conn_p, target_filename)); check_error(AFCConnectionClose(afc_conn_p)); }

// Handles the READ_DIR_AFTER_DIR callback so that we delete the contents of the // directory before the directory itself void rmtree_callback(AFCConnectionRef conn, const char *name, read_dir_cb_reason reason) { if (reason == READ_DIR_FILE || reason == READ_DIR_AFTER_DIR) { NSLogVerbose(@"Deleting %s", name); log_error(AFCRemovePath(conn, name)); } else if (reason == READ_DIR_FIFO) { NSLogVerbose(@"Skipping %s", name); } }

void rmtree(AMDeviceRef device) { AFCConnectionRef afc_conn_p = start_house_arrest_service(device); assert(afc_conn_p); read_dir(afc_conn_p, target_filename, rmtree_callback); check_error(AFCConnectionClose(afc_conn_p)); }

void uninstall_app(AMDeviceRef device) { CFRetain(device); // don't know if this is necessary?

NSLogOut(@"------ Uninstall phase ------");

//Do we already have the bundle_id passed in via the command line? if so, use it.
CFStringRef cf_uninstall_bundle_id = NULL;
if (bundle_id != NULL)
{
    cf_uninstall_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
} else {
    on_error(@"Error: you need to pass in the bundle id, (i.e. --bundle_id com.my.app)");
}

if (cf_uninstall_bundle_id == NULL) {
    on_error(@"Error: Unable to get bundle id from user command or package %@.\nUninstall failed.", [NSString stringWithUTF8String:app_path]);
} else {
    connect_and_start_session(device);

    int code = AMDeviceSecureUninstallApplication(0, device, cf_uninstall_bundle_id, 0, NULL, 0);
    if (code == 0) {
        NSLogOut(@"[ OK ] Uninstalled package with bundle id %@", cf_uninstall_bundle_id);
    } else {
        on_error(@"[ ERROR ] Could not uninstall package with bundle id %@", cf_uninstall_bundle_id);
    }
    CFRelease(cf_uninstall_bundle_id);
    check_error(AMDeviceStopSession(device));
    check_error(AMDeviceDisconnect(device));
}

}

if defined(IOS_DEPLOY_FEATURE_DEVELOPER_MODE)

void check_developer_mode(AMDeviceRef device) { unsigned int error_code = 0; bool is_enabled = AMDeviceCopyDeveloperModeStatus(device, &error_code);

if (error_code) { const char mobdev_error = get_error_message(error_code); NSString error_description = mobdev_error ? [NSString stringWithUTF8String:mobdev_error] : @"unknown."; if (_json_output) { NSLogJSON(@{ @"Event": @"DeveloperMode", @"IsEnabled": @(is_enabled), @"Code": @(error_code), @"Status": error_description, }); } else { NSLogOut(@"Encountered error checking developer mode status: %@", error_description); } } else { if (_json_output) { NSLogJSON(@{@"Event": @"DeveloperMode", @"IsEnabled": @(is_enabled)}); } else { NSLogOut(@"Developer mode is%s enabled.", is_enabled ? "" : " not"); } } }

endif

ServiceConnRef symbolsServiceConnection = NULL;

void start_symbols_service_with_command(AMDeviceRef device, uint32_t command) { connect_and_start_session(device); check_error(AMDeviceSecureStartService(device, symbols_service_name, NULL, &symbolsServiceConnection));

uint32_t bytes_sent = AMDServiceConnectionSend(symbolsServiceConnection, &command,
                                                sizeof_uint32_t);
if (bytes_sent != sizeof_uint32_t) {
    on_error(@"Sent %d bytes but was expecting %d.", bytes_sent, sizeof_uint32_t);
}

uint32_t response;
uint32_t bytes_read = AMDServiceConnectionReceive(symbolsServiceConnection,
                                                    &response, sizeof_uint32_t);
if (bytes_read != sizeof_uint32_t) {
    on_error(@"Read %d bytes but was expecting %d.", bytes_read, sizeof_uint32_t);
} else if (response != command) {
    on_error(@"Failed to get confirmation response for: %s", command);
}

}

CFArrayRef get_dyld_file_paths(AMDeviceRef device) { start_symbols_service_with_command(device, symbols_file_paths_command);

CFPropertyListFormat format;
CFDictionaryRef dict = NULL;
uint64_t bytes_read =
    AMDServiceConnectionReceiveMessage(symbolsServiceConnection, &dict, &format);
if (bytes_read == -1) {
    on_error(@"Received %d bytes after succesfully starting command %d.", bytes_read,
             symbols_file_paths_command);
}
AMDeviceStopSession(device);
AMDeviceDisconnect(device);

CFStringRef files_key = CFSTR("files");
if (!CFDictionaryContainsKey(dict, files_key)) {
    on_error(@"Incoming messasge did not contain key '%@', %@", files_key, dict);
}
return CFDictionaryGetValue(dict, files_key);

}

void write_dyld_file(CFStringRef dest, uint64_t file_size) { // Prepare the destination file by mapping it into memory. int fd = open(CFStringGetCStringPtr(dest, kCFStringEncodingUTF8), O_RDWR | O_CREAT, 0644); if (fd == -1) { on_sys_error(@"Failed to open %@.", dest); } if (lseek(fd, file_size - 1, SEEK_SET) == -1) { on_sys_error(@"Failed to lseek to last byte."); } if (write(fd, "", 1) == -1) { on_sys_error(@"Failed to write to last byte."); } void *map = mmap(NULL, file_size, PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { on_sys_error(@"Failed to mmap %@.", dest); } close(fd);

// Read the file content packet by packet until we've copied the entire file
// to disk.
uint64_t total_bytes_read = 0;
uint64_t last_time =
    get_current_time_in_milliseconds() / symbols_logging_interval_ms;
while (total_bytes_read < file_size) {
    uint64_t bytes_remaining = file_size - total_bytes_read;
    // This fails for some reason if we try to download more than
    // INT_MAX bytes at a time.
    uint64_t bytes_to_download = MIN(bytes_remaining, INT_MAX - 1);
    uint64_t bytes_read = AMDServiceConnectionReceive(
        symbolsServiceConnection, map + total_bytes_read, bytes_to_download);
    total_bytes_read += bytes_read;

    uint64_t current_time =
        get_current_time_in_milliseconds() / symbols_logging_interval_ms;
    // We can process several packets per second which would result
    // in spamming output so only log if any of the following are
    // true:
    //    - Running in verbose mode.
    //    - It's been at least a quarter second since the last log.
    //    - We finished processing the last packet.
    if (verbose || last_time != current_time || total_bytes_read == file_size) {
        last_time = current_time;
        int percent = (double)total_bytes_read / file_size * 100;
        NSLogOut(@"%llu/%llu (%d%%)", total_bytes_read, file_size, percent);
        NSLogJSON(@{@"Event": @"DyldCacheDownloadProgress",
                     @"BytesRead": @(total_bytes_read),
                     @"Percent": @(percent),
                  });
    }
}

munmap(map, file_size);

}

CFStringRef download_dyld_file(AMDeviceRef device, uint32_t dyld_index, CFStringRef filepath) { start_symbols_service_with_command(device, symbols_download_file_command);

uint32_t index = CFSwapInt32HostToBig(dyld_index);
uint64_t bytes_sent =
    AMDServiceConnectionSend(symbolsServiceConnection, &index, sizeof_uint32_t);
if (bytes_sent != sizeof_uint32_t) {
    on_error(@"Sent %d bytes but was expecting %d.", bytes_sent, sizeof_uint32_t);
}

uint64_t file_size = 0;
uint64_t bytes_read = AMDServiceConnectionReceive(symbolsServiceConnection,
                                                &file_size, sizeof(uint64_t));
if (bytes_read != sizeof(uint64_t)) {
    on_error(@"Read %d bytes but was expecting %d.", bytes_read, sizeof(uint64_t));
}
file_size = CFSwapInt64BigToHost(file_size);

CFStringRef download_path = CFStringCreateWithFormat(
    NULL, NULL, CFSTR("%s%@"), symbols_download_directory, filepath);
mkdirp(
    ((__bridge NSString *)download_path).stringByDeletingLastPathComponent);
NSLogOut(@"Downloading %@ to %@.", filepath, download_path);
NSLogJSON(@{@"Event": @"DyldCacheDownload",
             @"Source": (__bridge NSString *)filepath,
             @"Destination": (__bridge NSString *)download_path,
             @"Size": @(file_size),
          });

write_dyld_file(download_path, file_size);

AMDeviceStopSession(device);
AMDeviceDisconnect(device);
return download_path;

}

CFStringRef create_dsc_bundle_path_for_device(AMDeviceRef device) { CFStringRef xcode_dev_path = copy_xcode_dev_path();

is_usb_device(device) ? AMDeviceConnect(device) : connect_and_start_session(device);
CFStringRef device_class = AMDeviceCopyValue(device, 0, CFSTR("DeviceClass"));
AMDeviceDisconnect(device);
if (!device_class) {
  on_error(@"Failed to determine device class");
}

CFStringRef platform_name;
if (CFStringCompare(CFSTR("AppleTV"), device_class, 0) == kCFCompareEqualTo) {
    platform_name = CFSTR("AppleTVOS");
} else if (CFStringCompare(CFSTR("Watch"), device_class, 0) ==
           kCFCompareEqualTo) {
    platform_name = CFSTR("WatchOS");
} else {
    platform_name = CFSTR("iPhoneOS");
}

return CFStringCreateWithFormat(
    NULL, NULL,
    CFSTR("%@/Platforms/%@.platform/usr/lib/dsc_extractor.bundle"),
    xcode_dev_path, platform_name);

}

typedef int (extractor_proc)(const char shared_cache_file_path, const char *extraction_root_path, void (^progress)(unsigned current, unsigned total));

void dyld_shared_cache_extract_dylibs(CFStringRef dsc_extractor_bundle_path, CFStringRef shared_cache_file_path, const char extraction_root_path) { const char dsc_extractor_bundle_path_ptr = CFStringGetCStringPtr(dsc_extractor_bundle_path, kCFStringEncodingUTF8); void *handle = dlopen(dsc_extractor_bundle_path_ptr, RTLD_LAZY); if (handle == NULL) { on_error(@"%s could not be loaded", dsc_extractor_bundle_path); }

extractor_proc proc = (extractor_proc)dlsym(
    handle, "dyld_shared_cache_extract_dylibs_progress");
if (proc == NULL) {
    on_error(
        @"%s did not have dyld_shared_cache_extract_dylibs_progress symbol",
        dsc_extractor_bundle_path);
}

const char *shared_cache_file_path_ptr =
    CFStringGetCStringPtr(shared_cache_file_path, kCFStringEncodingUTF8);

NSLogJSON(@{@"Event": @"DyldCacheExtract",
             @"Source": (__bridge NSString *)shared_cache_file_path,
             @"Destination": @(extraction_root_path),
          });

__block uint64_t last_time =
    get_current_time_in_milliseconds() / symbols_logging_interval_ms;
__block unsigned files_extracted = 0;
__block unsigned files_total = 0;
int result =
    (*proc)(shared_cache_file_path_ptr, extraction_root_path,
            ^(unsigned c, unsigned total) {
          uint64_t current_time =
              get_current_time_in_milliseconds() / symbols_logging_interval_ms;
          if (!verbose && last_time == current_time) return;

          last_time = current_time;
          files_extracted = c;
          files_total = total;

          int percent = (double)c / total * 100;
          NSLogOut(@"%d/%d (%d%%)", c, total, percent);
          NSLogJSON(@{@"Event": @"DyldCacheExtractProgress",
                       @"Extracted": @(c),
                       @"Total": @(total),
                       @"Percent": @(percent),
                    });
    });
if (result == 0) {
    NSLogOut(@"Finished extracting %s.", shared_cache_file_path_ptr);
    files_extracted = files_total;
} else {
    NSLogOut(@"Failed to extract %s, exit code %d.", shared_cache_file_path_ptr, result);
}
int percent = (double)files_extracted / files_total * 100;
NSLogJSON(@{@"Event": @"DyldCacheExtractProgress",
            @"Code": @(result),
            @"Extracted": @(files_extracted),
            @"Total": @(files_total),
            @"Percent": @(percent),
          });

}

void download_device_symbols(AMDeviceRef device) { symbolsServiceConnection = NULL; CFArrayRef files = get_dyld_file_paths(device); CFIndex files_count = CFArrayGetCount(files); NSLogOut(@"Downloading symbols files: %@", files); NSLogJSON(@{@"Event": @"SymbolsDownload", @"Files": (__bridge NSArray *)files, }); CFStringRef dsc_extractor_bundle = create_dsc_bundle_path_for_device(device); CFMutableArrayRef downloaded_files = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);

// download files
for (uint32_t i = 0; i < files_count; ++i) {
    CFStringRef filepath = (CFStringRef)CFArrayGetValueAtIndex(files, i);
    CFStringRef download_path = download_dyld_file(device, i, filepath);
    CFArrayAppendValue(downloaded_files, download_path);
}
// extract files
for (uint32_t i = 0; i < files_count; ++i) {
    CFStringRef download_path = (CFStringRef)CFArrayGetValueAtIndex(downloaded_files, i);
    dyld_shared_cache_extract_dylibs(dsc_extractor_bundle, download_path,
                                         symbols_download_directory);
    CFRelease(download_path);
}

CFRelease(downloaded_files);
CFRelease(dsc_extractor_bundle);

}

typedef struct { uint32_t magic; uint32_t cb; uint16_t fragmentId; uint16_t fragmentCount; uint32_t length; uint32_t identifier; uint32_t conversationIndex; uint32_t channelCode; uint32_t expectsReply; } DTXMessageHeader;

typedef struct { uint32_t flags; uint32_t auxiliaryLength; uint64_t totalLength; } DTXMessagePayloadHeader;

uint32_t instruments_current_message_id = 0;

ServiceConnRef instrumentsServiceConnection = NULL; NSDictionary<NSString, NSNumber>* instruments_available_channels = nil;

static const uint32 DTXMessageHeaderMagic = 0x1F3D5B79; static const uint64 DTXAuxillaryDataMagic = 0x1F0;

static const uint32 EmptyDictionaryKey = 10; static const uint32 ObjectArgumentType = 2; static const uint32 Int32ArgumentType = 3; static const uint32 Int64ArgumentType = 4;

NSData instruments_object_argument(void argument) { NSError error = nil; NSData argumentData = [NSKeyedArchiver archivedDataWithRootObject:argument]; if (error) { on_error(@"Error communicating with the intruments server: %@", error); } uint32 argumentSize = (uint32) argumentData.length; NSMutableData *data = NSMutableData.data; [data appendBytes:&EmptyDictionaryKey length:sizeof(EmptyDictionaryKey)]; [data appendBytes:&ObjectArgumentType length:sizeof(ObjectArgumentType)]; [data appendBytes:&argumentSize length:sizeof(argumentSize)]; [data appendData:argumentData]; return data; }

NSData instruments_int32_argument(int32_t value) { NSMutableData data = NSMutableData.data; [data appendBytes:&EmptyDictionaryKey length:sizeof(EmptyDictionaryKey)]; [data appendBytes:&Int32ArgumentType length:sizeof(Int32ArgumentType)]; [data appendBytes:&value length:sizeof(value)]; return data; }

NSData instruments_int64_argument(int64_t value) { NSMutableData data = NSMutableData.data; [data appendBytes:&EmptyDictionaryKey length:sizeof(EmptyDictionaryKey)]; [data appendBytes:&Int64ArgumentType length:sizeof(Int64ArgumentType)]; [data appendBytes:&value length:sizeof(value)]; return data; }

void instruments_send_message(int channel, NSString selector, const NSArray<NSData> *args, bool expects_reply) { uint32_t id = ++instruments_current_message_id;

// Serialize arguments
NSMutableData *auxillaryData = NSMutableData.data;
if (args != nil) {
    NSMutableData *argumentsData = NSMutableData.data;
    for (NSData *argument in args) {
        [argumentsData appendData:argument];
    }

    uint64 payloadLength = argumentsData.length;
    [auxillaryData appendBytes:&DTXAuxillaryDataMagic length:sizeof(DTXAuxillaryDataMagic)];
    [auxillaryData appendBytes:&payloadLength length:sizeof(payloadLength)];
    [auxillaryData appendData:argumentsData];
}

// Serialize selector
NSError *error = nil;
NSData *selectorData = [NSKeyedArchiver archivedDataWithRootObject:selector];
if (error) {
    on_error(@"Error communicating with the intruments server: %@", error);
}

// Prepare the message
DTXMessagePayloadHeader payloadHeader;
payloadHeader.flags = 0x2 | (expects_reply ? 0x1000 : 0);
payloadHeader.auxiliaryLength = (uint32) auxillaryData.length;
payloadHeader.totalLength = auxillaryData.length + selectorData.length;

DTXMessageHeader messageHeader;
messageHeader.magic = DTXMessageHeaderMagic;
messageHeader.cb = sizeof(DTXMessageHeader);
messageHeader.fragmentId = 0;
messageHeader.fragmentCount = 1;
messageHeader.length = (uint32_t)(sizeof(payloadHeader) + payloadHeader.totalLength);
messageHeader.identifier = id;
messageHeader.conversationIndex = 0;
messageHeader.channelCode = channel;
messageHeader.expectsReply = (expects_reply ? 1 : 0);

NSMutableData *data = NSMutableData.data;
[data appendBytes:&messageHeader length:sizeof(messageHeader)];
[data appendBytes:&payloadHeader length:sizeof(payloadHeader)];
[data appendData:auxillaryData];
[data appendData:selectorData];

// Send message
uint64_t bytes_sent = AMDServiceConnectionSend(instrumentsServiceConnection, data.bytes, data.length);
if (bytes_sent != data.length) {
    on_error(@"Error communicating with instruments server");
}

}

NSArray instruments_parse_auxillary_data(NSData data) { if (data == nil) { return nil; }

if (data.length < 16) {
    on_error(@"Insufficient data to parse");
}

uint64_t offset = sizeof(uint64_t);

uint64_t payloadLength;
[data getBytes:&payloadLength range:NSMakeRange(offset, sizeof(payloadLength))];
offset += sizeof(payloadLength);

uint64_t end = offset + payloadLength;

NSMutableArray<id> *arguments = NSMutableArray.array;

while (offset < end) {
    uint32 type = 0;
    [data getBytes:&type range:NSMakeRange(offset, sizeof(type))];
    offset += sizeof(type);

    uint32 length = 0;

    id value = nil;

    switch (type) {
        case 2:
            [data getBytes:&length range:NSMakeRange(offset, sizeof(length))];
            offset += sizeof(length);
            value = [NSKeyedUnarchiver unarchiveObjectWithData:[data subdataWithRange:NSMakeRange(offset, length)]];
            break;
        case 3:
        case 5:
        {
            int32_t intValue;
            [data getBytes:&intValue range:NSMakeRange(offset, sizeof(intValue))];
            offset += sizeof(intValue);
            value = [NSNumber numberWithInt:intValue];
            break;
        }
        case 4:
        case 6:
        {
            int64_t intValue;
            [data getBytes:&intValue range:NSMakeRange(offset, sizeof(intValue))];
            offset += sizeof(intValue);
            value = [NSNumber numberWithLongLong:intValue];
            break;
        }
        case 10:
            // Empty dictionary value, ignore
            continue;
        default:
            // Unknown
            break;
    }

    if (value == nil) {
        on_error(@"Error communicating with instruments server: Error parsing auxiliary data");
    }
    [arguments addObject:value];
    offset += length;
}

return [arguments retain];

}

void instruments_receive_message(id* returnValue, NSArray* auxillaryValues) { NSMutableData payloadData = NSMutableData.data;

DTXMessageHeader messageHeader;
do {
    uint32_t bytes_read = AMDServiceConnectionReceive(instrumentsServiceConnection, &messageHeader, sizeof(messageHeader));
    if (bytes_read != sizeof(messageHeader)) {
        on_error(@"Error communicating with instruments server: Error in reading response");
    }
    if (messageHeader.magic != DTXMessageHeaderMagic) {
        on_error(@"Error communicating with instruments server: Magic in response magic does not match");
    }
    if (messageHeader.conversationIndex == 0 && messageHeader.identifier < instruments_current_message_id) {
        // New message with unexpected identifier
        on_error(@"Error communicating with instruments server: response identifier %d is lower than the last request (%d)", messageHeader.identifier, instruments_current_message_id);
    }
    if (messageHeader.conversationIndex == 1 && messageHeader.identifier != instruments_current_message_id) {
        // This is a response, but the message id does not match
        on_error(@"Error communicating with instruments server: expected response to message id %d, got %d", instruments_current_message_id, messageHeader.identifier);
    }

    instruments_current_message_id = messageHeader.identifier;

    if (messageHeader.fragmentId == 0 && messageHeader.fragmentCount > 1) {
        // First message in multi-fragment message has only payload.
        continue;
    }

    void *buffer = alloca(messageHeader.length);
    void *buffer_current = buffer;
    uint32_t remaining_bytes = messageHeader.length;
    while (remaining_bytes > 0) {
        uint32_t bytes_read = AMDServiceConnectionReceive(instrumentsServiceConnection, buffer_current, remaining_bytes);
        if (bytes_read <= 0) {
            on_error(@"Error communicating with instruments server: Error in reading response");
        }

        buffer_current += bytes_read;
        remaining_bytes -= bytes_read;
    }

    [payloadData appendBytes:buffer length:messageHeader.length];
} while (messageHeader.fragmentId < messageHeader.fragmentCount - 1);

const DTXMessagePayloadHeader *payloadHeader = (const DTXMessagePayloadHeader *)payloadData.bytes;

if ((payloadHeader->flags & 0xFF000) >> 12) {
    on_error(@"Error communicating with instruments server: Compression is not supported");
}

// serialized object array is located just after payload header
NSData* auxillaryData = nil;
if (payloadHeader->auxiliaryLength) {
    auxillaryData = [payloadData subdataWithRange:NSMakeRange(sizeof(DTXMessagePayloadHeader), payloadHeader->auxiliaryLength)];
}
if (auxillaryValues != nil) {
    *auxillaryValues = instruments_parse_auxillary_data(auxillaryData);
}

// archived payload object appears after the auxiliary array
size_t returnValueDataLength = payloadHeader->totalLength - payloadHeader->auxiliaryLength;
NSData* returnValueData = nil;
if (returnValueDataLength) {
    returnValueData = [payloadData subdataWithRange:NSMakeRange(sizeof(DTXMessagePayloadHeader) + payloadHeader->auxiliaryLength, returnValueDataLength)];
}
if (returnValue != nil) {
    *returnValue = [NSKeyedUnarchiver unarchiveObjectWithData:returnValueData];
}

}

void instruments_perform_handshake() { NSDictionary* capabilities = @{ @"com.apple.private.DTXBlockCompression": @2, @"com.apple.private.DTXConnection": @1 };

instruments_send_message(
    0 /* channel */,
    @"_notifyOfPublishedCapabilities:",
    @[
        instruments_object_argument(capabilities)
    ] /* args */,
    false /* expectes_reply */
);

id returnValue = nil;
NSArray<id>* auxillaryValues = nil;
instruments_receive_message(&returnValue, &auxillaryValues);

if (![returnValue isKindOfClass:NSString.class] || ![returnValue isEqualToString:@"_notifyOfPublishedCapabilities:"]) {
    on_error(@"Error communicating with instruments server: unexpected response selector");
}

NSDictionary<NSString*, NSNumber*>* channels = auxillaryValues.firstObject;
if (![channels isKindOfClass:NSDictionary.class]) {
    on_error(@"Error communicating with instruments server: unexpected channel list type");
}

instruments_available_channels = [channels retain];

[returnValue release];
[auxillaryValues release];

}

id instruments_perform_selector(int channel, NSString selector, const NSArray<NSData> args) { instruments_send_message(channel, selector, args, true / expectes_reply */);

id returnValue = nil;
instruments_receive_message(&returnValue, nil);

return returnValue;

}

int32_t instruments_make_channel(NSString* identifier) { if (![instruments_available_channels objectForKey:identifier]) { on_error(@"Channel %@ not supported by the server", identifier); }

static int32_t channel_id = 0;
int32_t code = ++channel_id;

id returnValue = instruments_perform_selector(
    0 /* channel */,
    @"_requestChannelWithCode:identifier:",
    @[
        instruments_int32_argument(code),
        instruments_object_argument(identifier)
    ]
);

if (returnValue != nil) {
    on_error(@"Error: _requestChannelWithCode:identifier: returned %@", returnValue);
}

[returnValue release];

return code;

}

void instruments_connect_service(AMDeviceRef device) { connect_and_start_session(device); mount_developer_image(device);

// Check version similar to start_remote_debug_server
CFStringRef keys[] = { CFSTR("MinIPhoneVersion"), CFSTR("MinAppleTVVersion"), CFSTR("MinWatchVersion") };
CFStringRef values[] = { CFSTR("14.0"), CFSTR("14.0"), CFSTR("7.0") }; // Not sure about older watchOS versions
CFDictionaryRef version = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

bool useSecureProxy = AMDeviceIsAtLeastVersionOnPlatform(device, version);

// Start the instruments server
assert(instrumentsServiceConnection == NULL);
CFStringRef serviceName = useSecureProxy ? CFSTR("com.apple.instruments.remoteserver.DVTSecureSocketProxy") : CFSTR("com.apple.instruments.remoteserver");
check_error(AMDeviceSecureStartService(device, serviceName, NULL, &instrumentsServiceConnection));

assert(instrumentsServiceConnection != NULL);

if (!useSecureProxy)
{
    disable_ssl(instrumentsServiceConnection);
}

check_error(AMDeviceStopSession(device));
check_error(AMDeviceDisconnect(device));

}

NSNumber pid_for_bundle_id(NSString bundle_id) { int32_t channel = instruments_make_channel(@"com.apple.instruments.server.services.processcontrol");

id pid = instruments_perform_selector(channel, @"processIdentifierForBundleIdentifier:", @[instruments_object_argument(bundle_id)]);

if (pid != nil && ![pid isKindOfClass:NSNumber.class]) {
    on_error(@"Error: did not get valid response from processIdentifierForBundleIdentifier:");
}

// Return -1 if pid is not found
return (pid == nil || [pid isEqualToNumber:@0]) ? @-1 : pid;

}

void get_pid(AMDeviceRef device) { if (bundle_id == NULL) { on_error(@"Error: bundle id required, please specify with --bundle_id."); }

instruments_connect_service(device);
instruments_perform_handshake();

CFStringRef cf_bundle_id = CFAutorelease(CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8));

NSNumber* pid = pid_for_bundle_id((NSString*)cf_bundle_id);

NSLogOut(@"pid: %@", pid);
NSLogJSON(@{@"Event": @"GetPid",
            @"pid": pid});

}

void kill_app(AMDeviceRef device) { if (bundle_id == NULL && command_pid <= 0) { on_error(@"Error: must specify either --pid or --bundle_id"); }

instruments_connect_service(device);
instruments_perform_handshake();

NSNumber* ns_pid = [NSNumber numberWithInt:command_pid];
if (![ns_pid isGreaterThan:@0]) {
    CFStringRef cf_bundle_id = CFAutorelease(CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8));
    ns_pid = pid_for_bundle_id((NSString*)cf_bundle_id);

    if (![ns_pid isGreaterThan:@0]) {
        NSLogOut(@"Could not find pid for bundle '%@'. Nothing to kill.", cf_bundle_id);
        return;
    }
}

int32_t channel = instruments_make_channel(@"com.apple.instruments.server.services.processcontrol");

instruments_send_message(channel, @"killPid:", @[instruments_object_argument(ns_pid)], false /* expectes_reply */);
[ns_pid release];

}

void list_processes(AMDeviceRef device) { instruments_connect_service(device); instruments_perform_handshake();

int32_t channel = instruments_make_channel(@"com.apple.instruments.server.services.deviceinfo");

id processes = instruments_perform_selector(channel, @"runningProcesses", nil /* args */);

if (processes == nil || ![processes isKindOfClass:NSArray.class]) {
    on_error(@"Error: could not retrieve return value for runningProcesses");
}

if (bundle_id != NULL) {
    CFStringRef cf_bundle_id = CFAutorelease(CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8));
    NSNumber* pid = pid_for_bundle_id((NSString*)cf_bundle_id);

    NSMutableArray* filteredProcesses = NSMutableArray.array;

    if ([pid isGreaterThan:@0]) {
        for (NSDictionary* proc in processes) {
            NSNumber* procPid = proc[@"pid"];
            if (procPid == pid) {
                [filteredProcesses addObject:proc];
            }
        }
    }

    [processes release];
    processes = filteredProcesses;
}

if (_json_output) {
    // NSDate cannot be serialized to JSON as is, manually convert it it NSString
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS'Z'"];
    [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];

    NSMutableArray* processesCopy = NSMutableArray.array;
    for (NSDictionary* proc in processes) {
        NSMutableDictionary* procCopy = [NSMutableDictionary dictionaryWithDictionary:proc];

        if (procCopy[@"startDate"] != nil) {
            NSString *startDate = [dateFormatter stringFromDate:procCopy[@"startDate"]];
            [procCopy removeObjectForKey:@"startDate"];
            [procCopy setObject:startDate forKey:@"startDate"];
        }

        [processesCopy addObject:procCopy];
    }

    NSLogJSON(@{@"Event": @"ListProcesses",
                @"Processes": processesCopy});
}
else {
    NSLogOut(@"PID\tNAME");
    for (NSDictionary* proc in processes) {
        NSLogOut(@"%@\t%@", proc[@"pid"], proc[@"name"]);
    }
}

[processes release];

}

void handle_device(AMDeviceRef device) { NSLogVerbose(@"Already found device? %d", found_device);

CFStringRef device_full_name = get_device_full_name(device),
            device_interface_name = get_device_interface_name(device);

if (detect_only) {
    if (_json_output) {
        NSLogJSON(@{@"Event": @"DeviceDetected",
                    @"Interface": (__bridge NSString *)device_interface_name,
                    @"Device": get_device_json_dict(device)
                    });
    } else {
        NSLogOut(@"[....] Found %@ connected through %@.", device_full_name, device_interface_name);
    }
    found_device = true;
    return;
}
if (found_device)
{
    NSLogVerbose(@"Skipping %@.", device_full_name);
    return;
}
CFStringRef found_device_id = CFAutorelease(AMDeviceCopyDeviceIdentifier(device));
if (device_id != NULL) {
    CFStringRef deviceCFSTR = CFAutorelease(CFStringCreateWithCString(NULL, device_id, kCFStringEncodingUTF8));
    if (CFStringCompare(deviceCFSTR, found_device_id, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
        found_device = true;
    } else {
        NSLogVerbose(@"Skipping %@.", device_full_name);
        return;
    }
} else {
    // Use the first device we find if a device_id wasn't specified.
    device_id = strdup(CFStringGetCStringPtr(found_device_id, kCFStringEncodingUTF8));
    found_device = true;
}

NSLogOut(@"[....] Using %@.", device_full_name);

if (command_only) {
    if (strcmp("list", command) == 0) {
        list_files(device);
    } else if (strcmp("upload", command) == 0) {
        upload_file(device);
    } else if (strcmp("download", command) == 0) {
        download_tree(device);
    } else if (strcmp("mkdir", command) == 0) {
        make_directory(device);
    } else if (strcmp("rm", command) == 0) {
        remove_path(device);
    } else if (strcmp("rmtree", command) == 0) {
        rmtree(device);
    } else if (strcmp("exists", command) == 0) {
        exit(app_exists(device));
    } else if (strcmp("uninstall_only", command) == 0) {
        uninstall_app(device);
    } else if (strcmp("list_bundle_id", command) == 0) {
        list_bundle_id(device);
    } else if (strcmp("list_processes", command) == 0) {
        list_processes(device);
    } else if (strcmp("get_pid", command) == 0) {
        get_pid(device);
    } else if (strcmp("get_battery_level", command) == 0) {
        get_battery_level(device);
    } else if (strcmp("symbols", command) == 0) {
        download_device_symbols(device);
    } else if (strcmp("list_profiles", command) == 0) {
        list_provisioning_profiles(device);
    } else if (strcmp("install_profile", command) == 0) {
        install_provisioning_profile(device);
    } else if (strcmp("uninstall_profile", command) == 0) {
        uninstall_provisioning_profile(device);
    } else if (strcmp("download_profile", command) == 0) {
        download_provisioning_profile(device);

if defined(IOS_DEPLOY_FEATURE_DEVELOPER_MODE)

    } else if (strcmp("check_developer_mode", command) == 0) {
        check_developer_mode(device);

endif

    } else if (strcmp("kill_app", command) == 0) {
        kill_app(device);
    }
    exit(0);
}

if (debugserver_only && app_path == NULL) {
    launch_debugserver_only(device, NULL);
    return;
}

CFRetain(device); // don't know if this is necessary?

CFStringRef path = CFStringCreateWithCString(NULL, app_path, kCFStringEncodingUTF8);
CFURLRef relative_url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, false);
CFURLRef url = CFURLCopyAbsoluteURL(relative_url);

CFRelease(relative_url);

if (uninstall) {
    NSLogOut(@"------ Uninstall phase ------");

    //Do we already have the bundle_id passed in via the command line? if so, use it.
    CFStringRef cf_uninstall_bundle_id = NULL;
    if (bundle_id != NULL)
    {
        cf_uninstall_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
    } else {
        cf_uninstall_bundle_id = copy_bundle_id(url);
    }

    if (cf_uninstall_bundle_id == NULL) {
        on_error(@"Error: Unable to get bundle id from user command or package %@.\nUninstall failed.", [NSString stringWithUTF8String:app_path]);
    } else {
        connect_and_start_session(device);

        int code = AMDeviceSecureUninstallApplication(0, device, cf_uninstall_bundle_id, 0, NULL, 0);
        if (code == 0) {
            NSLogOut(@"[ OK ] Uninstalled package with bundle id %@", cf_uninstall_bundle_id);
        } else {
            on_error(@"[ ERROR ] Could not uninstall package with bundle id %@", cf_uninstall_bundle_id);
        }
        CFRelease(cf_uninstall_bundle_id);
        check_error(AMDeviceStopSession(device));
        check_error(AMDeviceDisconnect(device));
    }
}

if(install) {
    NSLogOut(@"------ Install phase ------");
    NSLogOut(@"[  0%%] Found %@ connected through %@, beginning install", device_full_name, device_interface_name);

    CFStringRef install_bundle_id = bundle_id == NULL ? copy_bundle_id(url) : CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);

    CFDictionaryRef options;
    if (app_deltas == NULL) { // standard install
      CFStringRef keys[] = { CFSTR("PackageType") };
      CFStringRef values[] = { CFSTR("Developer") };
      options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
      check_error(AMDeviceSecureTransferPath(0, device, url, options, transfer_callback, 0));

      connect_and_start_session(device);
      check_error(AMDeviceSecureInstallApplication(0, device, url, options, install_callback, 0));
      check_error(AMDeviceStopSession(device));
      check_error(AMDeviceDisconnect(device));
    } else { // incremental install
      if (install_bundle_id == NULL) {
        on_error(@"[ ERROR] Could not determine bundle id.");
      }
      CFStringRef deltas_path =
        CFStringCreateWithCString(NULL, app_deltas, kCFStringEncodingUTF8);
      CFURLRef deltas_relative_url =
        CFURLCreateWithFileSystemPath(NULL, deltas_path, kCFURLPOSIXPathStyle, false);
      CFURLRef app_deltas_url = CFURLCopyAbsoluteURL(deltas_relative_url);
      CFStringRef prefer_wifi = no_wifi ? CFSTR("0") : CFSTR("1");

      // These values were determined by inspecting Xcode 11.1 logs with the Console app.
      CFStringRef keys[] = {
        CFSTR("CFBundleIdentifier"),
        CFSTR("CloseOnInvalidate"),
        CFSTR("InvalidateOnDetach"),
        CFSTR("IsUserInitiated"),
        CFSTR("PackageType"),
        CFSTR("PreferWifi"),
        CFSTR("ShadowParentKey"),
      };
      CFStringRef values[] = {
        install_bundle_id,
        CFSTR("1"),
        CFSTR("1"),
        CFSTR("1"),
        CFSTR("Developer"),
        prefer_wifi,
        (CFStringRef)app_deltas_url,
      };

      CFIndex size = sizeof(keys)/sizeof(CFStringRef);
      options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, size, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

      // Incremental installs should be done without a session started because of timeouts.
      check_error(AMDeviceSecureInstallApplicationBundle(device, url, options, incremental_install_callback, 0));
      CFRelease(deltas_path);
      CFRelease(deltas_relative_url);
      CFRelease(app_deltas_url);
      free(app_deltas);
      app_deltas = NULL;
    }

    CFRelease(options);

    NSLogOut(@"[100%%] Installed package %@", [NSString stringWithUTF8String:app_path]);
    if (install_bundle_id == NULL) {
      NSLogJSON(@{@"Event": @"BundleInstall",
                  @"OverallPercent": @(100),
                  @"Percent": @(100),
                  @"Status": @"Complete"
                  });
    } else {
      connect_and_start_session(device);
      CFURLRef device_app_url = copy_device_app_url(device, install_bundle_id);
      check_error(AMDeviceStopSession(device));
      check_error(AMDeviceDisconnect(device));
      CFStringRef device_app_path = CFURLCopyFileSystemPath(device_app_url, kCFURLPOSIXPathStyle);

      NSLogVerbose(@"App path: %@", device_app_path);
      NSLogJSON(@{@"Event": @"BundleInstall",
                  @"OverallPercent": @(100),
                  @"Percent": @(100),
                  @"Status": @"Complete",
                  @"Path": (__bridge NSString *)device_app_path
                  });

      CFRelease(device_app_url);
      CFRelease(install_bundle_id);
      CFRelease(device_app_path);
    }
}
CFRelease(path);

if (!debug)
    exit(0); // no debug phase

if (justlaunch) {
    launch_debugger_and_exit(device, url);
} else if (debugserver_only) {
    launch_debugserver_only(device, url);
} else {
    launch_debugger(device, url);
}

}

void log_device_disconnected(AMDeviceRef device) { CFStringRef device_interface_name = get_device_interface_name(device); CFStringRef device_uuid = CFAutorelease(AMDeviceCopyDeviceIdentifier(device));

if (_json_output) {
    NSLogJSON(@{@"Event": @"DeviceDisconnected",
                @"Interface": (__bridge NSString *)device_interface_name,
                @"Device": get_device_json_dict(device)
                });
}
else {
    NSLogOut(@"[....] Disconnected %@ from %@.", device_uuid, device_interface_name);
}

}

void handle_device_disconnected(AMDeviceRef device) { CFStringRef device_interface_name = get_device_interface_name(device); CFStringRef device_uuid = AMDeviceCopyDeviceIdentifier(device);

if (detect_only) {
    log_device_disconnected(device);
}
else {
    NSLogVerbose(@"[....] Disconnected %@ from %@.", device_uuid, device_interface_name);
}

if (debugserver_only) {
    CFStringRef deviceCFSTR = CFAutorelease(CFStringCreateWithCString(NULL, device_id, kCFStringEncodingUTF8));
    if (CFStringCompare(deviceCFSTR, device_uuid, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
        log_device_disconnected(device);
        exit(0);
    }
}

CFRelease(device_uuid);

}

void device_callback(struct am_device_notification_callback_info info, void arg) { switch (info->msg) { case ADNCI_MSG_CONNECTED: { int itype = AMDeviceGetInterfaceType(info->dev); if (no_wifi && (itype == 2 || ( itype == 3 && get_companion_interface_type(info->dev) == 2))) { NSLogVerbose(@"Skipping wifi device (type: %d)", itype); } else { NSLogVerbose(@"Handling device type: %d", itype); handle_device(info->dev); } break; } case ADNCI_MSG_DISCONNECTED: { handle_device_disconnected(info->dev); break; } default: break; } }

void timeout_callback(CFRunLoopTimerRef timer, void *info) { if (found_device && (!detect_only)) { // Don't need to exit in the justlaunch mode if (justlaunch) return;

    // App running for too long
    NSLogOut(@"[ !! ] App is running for too long");
    exit(exitcode_timeout);
    return;
} else if ((!found_device) && (!detect_only))  {
    on_error(@"Timed out waiting for device.");
}
else
{
    // Device detection timeout
    if (!debug) {
        NSLogOut(@"[....] No more devices found.");
    }

    if (detect_only && !found_device) {
        exit(exitcode_error);
        return;
    } else {
        int mypid = getpid();
        if ((parent != 0) && (parent == mypid) && (child != 0))
        {
            NSLogVerbose(@"Timeout. Killing child (%d) tree.", child);
            kill_ptree(child, SIGHUP);
        }
    }
    exit(0);
}

}

void usage(const char* app) { NSLog( @"Usage: %@ [OPTION]...\n" @" -d, --debug launch the app in lldb after installation\n" @" -i, --id the id of the device to connect to\n" @" -c, --detect list all connected devices\n" @" -b, --bundle the path to the app bundle to be installed\n" @" -a, --args command line arguments to pass to the app when launching it\n" @" -s, --envs environment variables, space separated key-value pairs, to pass to the app when launching it\n" @" -t, --timeout number of seconds to wait for a device to be connected\n" @" -u, --unbuffered don't buffer stdout\n" @" -n, --nostart do not start the app when debugging\n" @" -N, --nolldb start debugserver only. do not run lldb. Can not be used with args or envs options\n" @" -I, --noninteractive start in non interactive mode (quit when app crashes or exits)\n" @" -L, --justlaunch just launch the app and exit lldb\n" @" -v, --verbose enable verbose output\n" @" -m, --noinstall directly start debugging without app install (-d not required)\n" @" -A, --app_deltas incremental install. must specify a directory to store app deltas to determine what needs to be installed\n" @" -p, --port port used for device, default: dynamic\n" @" -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared) \n" @" -9, --uninstall_only uninstall the app ONLY. Use only with -1 \n" @" -1, --bundle_id specify bundle id for list and upload\n" @" -l, --list[=

] list all app files or the specified directory\n" @" -o, --upload upload file\n" @" -w, --download[=] download app tree or the specified file/directory\n" @" -2, --to use together with up/download file/tree. specify target\n" @" -D, --mkdir make directory on device\n" @" -R, --rm remove file or directory on device (directories must be empty)\n" @" -X, --rmtree remove directory and all contained files recursively on device\n" @" -V, --version print the executable version \n" @" -e, --exists check if the app with given bundle_id is installed or not \n" @" -B, --list_bundle_id list bundle_id \n" @" --list_processes list running processes \n" @" --get_pid get process id for the bundle. must specify --bundle_id\n" @" --pid specify pid, to be used with --kill\n" @" --kill kill a process. must specify either --pid or --bundle_id\n" @" -W, --no-wifi ignore wifi devices\n" @" -C, --get_battery_level get battery current capacity \n" @" -O, --output write stdout to this file\n" @" -E, --error_output write stderr to this file\n" @" --detect_deadlocks start printing backtraces for all threads periodically after specific amount of seconds\n" @" -f, --file_system specify file system for mkdir / list / upload / download / rm\n" @" -F, --non-recursively specify non-recursively walk directory\n" @" -S, --symbols download OS symbols. must specify a directory to store the downloaded symbols\n" @" -j, --json format output as JSON\n" @" -k, --key keys for the properties of the bundle. Joined by ',' and used only with -B and -j \n" @" --custom-script Githubissues.
  • Githubissues is a development platform for aggregating issues.