gpgme-0.11.0/.cargo_vcs_info.json0000644000000001360000000000100122120ustar { "git": { "sha1": "ed5519c8fe1e166daa1e23087f00a8d20a00f8e1" }, "path_in_vcs": "" }gpgme-0.11.0/.dockerignore000064400000000000000000000000371046102023000134560ustar 00000000000000**/.* **/Cargo.lock **/target/ gpgme-0.11.0/.github/workflows/ci.yml000064400000000000000000000034061046102023000155200ustar 00000000000000name: Continuous Integration on: [push, pull_request] jobs: test: name: Test Suite (${{ matrix.os }}, rust-${{ matrix.rust }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest rust: stable - os: ubuntu-latest rust: nightly - os: macos-latest rust: stable env: GPGME_DEBUG: 9 steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install dependencies (Linux) if: startsWith(matrix.os, 'ubuntu') run: sudo apt-get install -y --no-install-recommends libgpgme11-dev - name: Install dependencies (macOS) if: startsWith(matrix.os, 'macos') run: brew install gpgme - name: Install rust uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose --no-fail-fast docker-static: name: Test Suite (linux, docker, musl) runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Build docker container run: docker build -f docker/Dockerfile.static -t test-build . - name: Run tests in container run: docker run test-build docker-windows: name: Test Suite (windows, docker) runs-on: windows-2022 continue-on-error: true steps: - name: Checkout repository uses: actions/checkout@v3 - name: Build docker container run: docker build --build-arg WIN_VARIANT=ltsc2022 -f docker/Dockerfile.windows -t test-build . - name: Run tests in container run: docker run test-build gpgme-0.11.0/.gitignore000064400000000000000000000002001046102023000127620ustar 00000000000000.* !.gitignore !.gitmodules !.travis.yml # Compiled files *.o *.so *.rlib *.dll *.exe # Generated by Cargo Cargo.lock target/ gpgme-0.11.0/COPYING000064400000000000000000000636421046102023000120500ustar 00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! gpgme-0.11.0/Cargo.lock0000644000000273360000000000100102000ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "build-rs" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b00b8763668c99f8d9101b8a0dd82106f58265464531a79b2cef0d9a30c17dd2" [[package]] name = "cfg-expr" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" dependencies = [ "smallvec", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", "bitflags", "strsim", "textwrap", "unicode-width", "vec_map", ] [[package]] name = "conv" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" dependencies = [ "custom_derive", ] [[package]] name = "cstr-argument" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bd9c8e659a473bce955ae5c35b116af38af11a7acb0b480e01f3ed348aeb40" dependencies = [ "cfg-if", "memchr", ] [[package]] name = "custom_derive" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" [[package]] name = "fastrand" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "gpg-error" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89aaeddbfb92313378c58e98abadaaa34082b3855f1d455576eeeda08bd592c" dependencies = [ "libgpg-error-sys", ] [[package]] name = "gpgme" version = "0.11.0" dependencies = [ "bitflags", "cfg-if", "conv", "cstr-argument", "gpg-error", "gpgme-sys", "libc", "memoffset", "once_cell", "smallvec", "static_assertions", "structopt", "tempfile", ] [[package]] name = "gpgme-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "509223d659c06e4a26229437d6ac917723f02d31917c86c6ecd50e8369741cf7" dependencies = [ "build-rs", "libc", "libgpg-error-sys", "system-deps", "winreg", ] [[package]] name = "heck" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] [[package]] name = "heck" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "libgpg-error-sys" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c97079310f39c835d3bd73578379d040f779614bb331c7ffbb6630fee6420290" dependencies = [ "build-rs", "system-deps", "winreg", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "pkg-config" version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] [[package]] name = "serde" version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ "clap", "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "system-deps" version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" dependencies = [ "cfg-expr", "heck 0.4.0", "pkg-config", "toml", "version-compare", ] [[package]] name = "tempfile" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if", "fastrand", "libc", "redox_syscall", "remove_dir_all", "winapi", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "toml" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-segmentation" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version-compare" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winreg" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] gpgme-0.11.0/Cargo.toml0000644000000031400000000000100102060ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.64" name = "gpgme" version = "0.11.0" description = "GPGme bindings for Rust" documentation = "https://docs.rs/gpgme" readme = "README.md" keywords = [ "gpg", "gpgme", "crypto", "cryptography", ] categories = ["api-bindings"] license = "LGPL-2.1" repository = "https://github.com/gpg-rs/gpgme" [package.metadata.docs.rs] all-features = true [[bin]] name = "pinentry" test = false [dependencies.bitflags] version = "1" [dependencies.cfg-if] version = "1" [dependencies.conv] version = "0.3" [dependencies.cstr-argument] version = "0.1" [dependencies.ffi] version = "0.11.0" package = "gpgme-sys" [dependencies.gpg-error] version = "0.6.0" [dependencies.libc] version = "0.2" [dependencies.memoffset] version = "0.7.1" [dependencies.once_cell] version = "1" [dependencies.smallvec] version = "1" [dependencies.static_assertions] version = "1.1" [dev-dependencies.structopt] version = "0.3" [dev-dependencies.tempfile] version = "3" [features] v1_13 = [] v1_14 = ["v1_13"] v1_15 = ["v1_14"] v1_16 = ["v1_15"] v1_17 = ["v1_16"] v1_18 = ["v1_17"] [badges.maintenance] status = "experimental" gpgme-0.11.0/Cargo.toml.orig000064400000000000000000000021201046102023000136640ustar 00000000000000[package] name = "gpgme" version.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true repository.workspace = true documentation = "https://docs.rs/gpgme" categories = ["api-bindings"] keywords = ["gpg", "gpgme", "crypto", "cryptography"] description = "GPGme bindings for Rust" [package.metadata.docs.rs] all-features = true [badges] maintenance = { status = "experimental" } [features] "v1_18" = ["v1_17"] "v1_17" = ["v1_16"] "v1_16" = ["v1_15"] "v1_15" = ["v1_14"] "v1_14" = ["v1_13"] "v1_13" = [] [dev-dependencies] structopt = "0.3" tempfile = "3" [dependencies] bitflags = "1" cfg-if = "1" conv = "0.3" cstr-argument = "0.1" gpg-error = "0.6.0" libc.workspace = true memoffset = "0.7.1" once_cell = "1" smallvec = "1" static_assertions = "1.1" [dependencies.ffi] package = "gpgme-sys" path = "gpgme-sys" version = "0.11.0" [[bin]] name = "pinentry" test = false [workspace.package] version = "0.11.0" edition = "2021" rust-version = "1.64" license = "LGPL-2.1" repository = "https://github.com/gpg-rs/gpgme" [workspace.dependencies] libc = "0.2" gpgme-0.11.0/README.md000064400000000000000000000037701046102023000122700ustar 00000000000000# gpgme-rs [![Build Status][build]][ci] [![crates.io version][version]][crate] [![LGPL-2.1 licensed][license]](./COPYING) [![downloads][downloads]][crate] [GPGME][upstream] bindings for Rust. [Documentation][docs] ## Using To use the crate, add it to your depedencies: ```sh $ cargo add gpgme ``` ### Requirements These crates require the gpgme library (version 1.13 or later) and its development files to be installed. The build script uses the [system-deps] crate to attempt to locate them (or the registry on Windows). On Debian/Ubuntu based systems: ```sh $ sudo apt-get install libgpgme11-dev ``` On Fedora/RHEL based systems: ```sh $ sudo dnf install gpgme-devel ``` On MacOS systems: ```sh $ brew install gnupg ``` On Windows systems, download and install the official [Gpg4win] installer. Only the `i686-pc-windows-gnu` target is supported. **NOTE**: These crates also depend on the gpg-error crate which has its own [requirements](https://github.com/gpg-rs/libgpg-error). ## Examples Some simple example programs based on those in the GPGME sources can be found in [examples](./examples). They can be run with cargo: ```shell $ cargo run --example keylist -- keyid : 89ABCDEF01234567 fpr : 0123456789ABCDEF0123456789ABCDEF01234567 caps : esc flags : userid 0: Example valid 0: Unknown ``` ## License These crates are licensed under the [LGPL-2.1 license](./COPYING). [crate]: https://crates.io/crates/gpgme [ci]: https://github.com/gpg-rs/gpgme/actions?query=branch%3Amaster [build]: https://img.shields.io/github/workflow/status/gpg-rs/gpgme/Continuous%20Integration?style=flat-square [version]: https://img.shields.io/crates/v/gpgme?style=flat-square [license]: https://img.shields.io/crates/l/gpgme?style=flat-square [downloads]: https://img.shields.io/crates/d/gpgme?style=flat-square [upstream]: https://www.gnupg.org/\(it\)/related_software/gpgme/index.html [docs]: https://docs.rs/gpgme [system-deps]: https://crates.io/crates/system-deps [Gpg4win]: https://www.gpg4win.org/ gpgme-0.11.0/docker/Dockerfile.static000064400000000000000000000043171046102023000155360ustar 00000000000000FROM clux/muslrust:stable as builder RUN apt-get update && apt-get install -y --no-install-recommends bzip2 gnupg && rm -rf /var/lib/apt/lists/* ENV TARGET "x86_64-unknown-linux-musl" ENV SYSTEM_DEPS_LINK static # Optional localization support: # To enable uncomment the following commands, replace "--disable-nls" with # "--with-libintl-prefix=$PREFIX". # ARG GETTEXT_VER=0.21 # WORKDIR /usr/src # ADD https://ftp.gnu.org/gnu/gettext/gettext-${GETTEXT_VER}.tar.bz2 ./ # RUN tar -xjf gettext-${GETTEXT_VER}.tar.bz2 # WORKDIR gettext-$GETTEXT_VER # RUN ./configure --host "$TARGET" --prefix="$PREFIX" --with-pic --enable-fast-install --disable-dependency-tracking --without-emacs --disable-java --disable-csharp --disable-c++ # RUN make -j$(nproc) install ARG LIBGPG_ERROR_VER=1.46 WORKDIR /usr/src ADD https://www.gnupg.org/ftp/gcrypt/libgpg-error/libgpg-error-${LIBGPG_ERROR_VER}.tar.bz2 ./ RUN tar -xjf libgpg-error-${LIBGPG_ERROR_VER}.tar.bz2 WORKDIR libgpg-error-$LIBGPG_ERROR_VER RUN ./configure --host "$TARGET" --prefix="$PREFIX" --with-pic --enable-fast-install --disable-dependency-tracking --enable-static --disable-shared --disable-nls --disable-doc --disable-languages --disable-tests --enable-install-gpg-error-config RUN make -j$(nproc) install ARG LIBASSUAN_VER=2.5.5 WORKDIR /usr/src ADD https://www.gnupg.org/ftp/gcrypt/libassuan/libassuan-${LIBASSUAN_VER}.tar.bz2 ./ RUN tar -xjf libassuan-${LIBASSUAN_VER}.tar.bz2 WORKDIR libassuan-$LIBASSUAN_VER RUN ./configure --host "$TARGET" --prefix="$PREFIX" --with-pic --enable-fast-install --disable-dependency-tracking --enable-static --disable-shared --disable-doc --with-gpg-error-prefix="$PREFIX" RUN make -j$(nproc) install ARG GPGME_VER=1.18.0 WORKDIR /usr/src ADD https://www.gnupg.org/ftp/gcrypt/gpgme/gpgme-${GPGME_VER}.tar.bz2 ./ RUN tar -xjf gpgme-${GPGME_VER}.tar.bz2 WORKDIR gpgme-$GPGME_VER RUN ./configure --host "$TARGET" --prefix="$PREFIX" --with-pic --enable-fast-install --disable-dependency-tracking --enable-static --disable-shared --disable-languages --disable-gpg-test --with-gpg-error-prefix="$PREFIX" --with-libassuan-prefix="$PREFIX" RUN make -j$(nproc) install FROM builder WORKDIR /root/ws COPY ./ ./ CMD ["cargo", "test", "--no-fail-fast", "--all-features"] gpgme-0.11.0/docker/Dockerfile.windows000064400000000000000000000013111046102023000157300ustar 00000000000000# escape=` ARG WIN_VARIANT=1909 FROM mcr.microsoft.com/windows/servercore:${WIN_VARIANT} ENV RUSTUP_HOME=C:\rustup CARGO_HOME=C:\cargo SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] ADD https://win.rustup.rs/ C:\TEMP\rustup-init.exe RUN C:\TEMP\rustup-init.exe -y --profile minimal --default-host i686-pc-windows-gnu RUN setx /M PATH $(${Env:PATH} + \";${Env:CARGO_HOME}\bin\") ARG GNUPG_VERSION=2.3.8_20221013 ADD https://gnupg.org/ftp/gcrypt/binary/gnupg-w32-${GNUPG_VERSION}.exe C:\TEMP\gnupg-w32.exe RUN C:\TEMP\gnupg-w32.exe /S WORKDIR C:\workspace COPY ./ ./ ENV GPGME_DEBUG 9 CMD ["cargo", "test", "--no-fail-fast", "--all-features"] gpgme-0.11.0/examples/decrypt.rs000064400000000000000000000015641046102023000146460ustar 00000000000000use structopt; use gpgme::{Context, Protocol}; use std::{ error::Error, fs::File, io::{self, prelude::*}, path::PathBuf, }; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long)] /// Use the CMS protocol cms: bool, #[structopt(parse(from_os_str))] /// File to decrypt filename: PathBuf, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let proto = if args.cms { Protocol::Cms } else { Protocol::OpenPgp }; let mut ctx = Context::from_protocol(proto)?; let mut input = File::open(&args.filename)?; let mut output = Vec::new(); ctx.decrypt(&mut input, &mut output) .map_err(|e| format!("decrypting failed: {:?}", e))?; println!("Begin Output:"); io::stdout().write_all(&output)?; println!("End Output."); Ok(()) } gpgme-0.11.0/examples/encrypt.rs000064400000000000000000000025421046102023000146550ustar 00000000000000use structopt; use gpgme::{Context, Protocol}; use std::{ error::Error, fs::File, io::{self, prelude::*}, path::PathBuf, }; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long)] /// Use the CMS protocol cms: bool, #[structopt(short, long = "recipient")] /// For whom to encrypt the messages recipients: Vec, #[structopt(parse(from_os_str))] /// Files to encrypt filename: PathBuf, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let proto = if args.cms { Protocol::Cms } else { Protocol::OpenPgp }; let mut ctx = Context::from_protocol(proto)?; ctx.set_armor(true); let keys = if !args.recipients.is_empty() { ctx.find_keys(args.recipients)? .filter_map(|x| x.ok()) .filter(|k| k.can_encrypt()) .collect() } else { Vec::new() }; let filename = &args.filename; let mut input = File::open(&args.filename) .map_err(|e| format!("can't open file `{}': {:?}", filename.display(), e))?; let mut output = Vec::new(); ctx.encrypt(&keys, &mut input, &mut output) .map_err(|e| format!("encrypting failed: {:?}", e))?; println!("Begin Output:"); io::stdout().write_all(&output)?; println!("End Output."); Ok(()) } gpgme-0.11.0/examples/export.rs000064400000000000000000000031031046102023000145040ustar 00000000000000use structopt; use gpgme::{Context, ExportMode, Protocol}; use std::{ error::Error, io::{self, prelude::*}, }; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long = "extern")] /// Send keys to the keyserver external: bool, #[structopt(min_values(1))] /// Keys to export users: Vec, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let mode = if args.external { ExportMode::EXTERN } else { ExportMode::empty() }; let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; ctx.set_armor(true); let keys = { let mut key_iter = ctx.find_keys(args.users)?; let keys: Vec<_> = key_iter.by_ref().collect::>()?; for key in &keys { println!( "keyid: {} (fpr: {})", key.id().unwrap_or("?"), key.fingerprint().unwrap_or("?") ); } if key_iter.finish()?.is_truncated() { Err("key listing unexpectedly truncated")?; } keys }; if mode.contains(ExportMode::EXTERN) { println!("sending keys to keyserver"); ctx.export_keys_extern(&keys, mode) .map_err(|e| format!("export failed: {:?}", e))?; } else { let mut output = Vec::new(); ctx.export_keys(&keys, mode, &mut output) .map_err(|e| format!("export failed: {:?}", e))?; println!("Begin Result:"); io::stdout().write_all(&output)?; println!("End Result."); } Ok(()) } gpgme-0.11.0/examples/import.rs000064400000000000000000000053231046102023000145030ustar 00000000000000use gpgme; use structopt; use gpgme::{data, Context, Data, ImportFlags}; use std::{error::Error, fs::File, path::PathBuf}; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long)] /// Import from given URLs url: bool, #[structopt(short = "0")] /// URLS are delimited by a null nul: bool, #[structopt(min_values(1), parse(from_os_str))] filenames: Vec, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let mode = if args.url { if args.nul { Some(data::Encoding::Url0) } else { Some(data::Encoding::Url) } } else { None }; let mut ctx = Context::from_protocol(gpgme::Protocol::OpenPgp)?; for file in args.filenames { println!("reading file `{}'", &file.display()); let input = File::open(file)?; let mut data = Data::from_seekable_stream(input)?; mode.map(|m| data.set_encoding(m)); print_import_result( ctx.import(&mut data) .map_err(|e| format!("import failed {:?}", e))?, ); } Ok(()) } fn print_import_result(result: gpgme::ImportResult) { for import in result.imports() { print!( " fpr: {} err: {:?} status:", import.fingerprint().unwrap_or("[none]"), import.result().err() ); let status = import.status(); if status.contains(ImportFlags::NEW) { print!(" new"); } if status.contains(ImportFlags::UID) { print!(" uid"); } if status.contains(ImportFlags::SIG) { print!(" sig"); } if status.contains(ImportFlags::SUBKEY) { print!(" subkey"); } if status.contains(ImportFlags::SECRET) { print!(" secret"); } println!(""); } println!("key import summary:"); println!(" considered: {}", result.considered()); println!(" no user id: {}", result.without_user_id()); println!(" imported: {}", result.imported()); println!(" imported rsa: {}", result.imported_rsa()); println!(" unchanged: {}", result.unchanged()); println!(" new user ids: {}", result.new_user_ids()); println!(" new subkeys: {}", result.new_subkeys()); println!(" new signatures: {}", result.new_signatures()); println!(" new revocations: {}", result.new_revocations()); println!(" secret read: {}", result.secret_considered()); println!(" secret imported: {}", result.secret_imported()); println!(" secret unchanged: {}", result.secret_unchanged()); println!(" not imported: {}", result.not_imported()); } gpgme-0.11.0/examples/keylist.rs000064400000000000000000000053211046102023000146530ustar 00000000000000use structopt; use gpgme::{Context, KeyListMode, Protocol}; use std::error::Error; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long)] /// Use the CMS protocol cms: bool, #[structopt(long)] /// Use GPGME_KEYLIST_MODE_LOCAL local: bool, #[structopt(long = "extern")] /// Use GPGME_KEYLIST_MODE_EXTERN external: bool, #[structopt(long)] /// Use GPGME_KEYLIST_MODE_SIGS sigs: bool, #[structopt(long = "sig-notations")] /// Use GPGME_KEYLIST_MODE_SIG_NOTATIONS notations: bool, #[structopt(long)] /// Use GPGME_KEYLIST_MODE_EPHEMERAL ephemeral: bool, #[structopt(long)] /// Use GPGME_KEYLIST_MODE_VALIDATE validate: bool, users: Vec, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let proto = if args.cms { Protocol::Cms } else { Protocol::OpenPgp }; let mut mode = KeyListMode::empty(); if args.local { mode.insert(KeyListMode::LOCAL); } if args.external { mode.insert(KeyListMode::EXTERN); } if args.sigs { mode.insert(KeyListMode::SIGS); } if args.notations { mode.insert(KeyListMode::SIG_NOTATIONS); } if args.ephemeral { mode.insert(KeyListMode::EPHEMERAL); } if args.validate { mode.insert(KeyListMode::VALIDATE); } let mut ctx = Context::from_protocol(proto)?; ctx.set_key_list_mode(mode)?; let mut keys = ctx.find_keys(args.users)?; for key in keys.by_ref().filter_map(|x| x.ok()) { println!("keyid : {}", key.id().unwrap_or("?")); println!("fpr : {}", key.fingerprint().unwrap_or("?")); println!( "caps : {}{}{}{}", if key.can_encrypt() { "e" } else { "" }, if key.can_sign() { "s" } else { "" }, if key.can_certify() { "c" } else { "" }, if key.can_authenticate() { "a" } else { "" } ); println!( "flags :{}{}{}{}{}{}", if key.has_secret() { " secret" } else { "" }, if key.is_revoked() { " revoked" } else { "" }, if key.is_expired() { " expired" } else { "" }, if key.is_disabled() { " disabled" } else { "" }, if key.is_invalid() { " invalid" } else { "" }, if key.is_qualified() { " qualified" } else { "" } ); for (i, user) in key.user_ids().enumerate() { println!("userid {}: {}", i, user.id().unwrap_or("[none]")); println!("valid {}: {:?}", i, user.validity()) } println!(""); } if keys.finish()?.is_truncated() { Err("key listing unexpectedly truncated")?; } Ok(()) } gpgme-0.11.0/examples/keysign.rs000064400000000000000000000022161046102023000146400ustar 00000000000000use structopt; use gpgme::{Context, Protocol}; use std::error::Error; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long)] /// Use the CMS protocol cms: bool, #[structopt(long)] /// Key to use for signing. Default key is used otherwise key: Option, /// Key to sign keyid: String, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let proto = if args.cms { Protocol::Cms } else { Protocol::OpenPgp }; let mut ctx = Context::from_protocol(proto)?; let key_to_sign = ctx .get_key(&args.keyid) .map_err(|e| format!("no key matched given key-id: {:?}", e))?; if let Some(key) = args.key { let key = ctx .get_secret_key(key) .map_err(|e| format!("unable to find signing key: {:?}", e))?; ctx.add_signer(&key) .map_err(|e| format!("add_signer() failed: {:?}", e))?; } ctx.sign_key(&key_to_sign, None::, Default::default()) .map_err(|e| format!("signing failed: {:?}", e))?; println!("Signed key for {}", args.keyid); Ok(()) } gpgme-0.11.0/examples/sign.rs000064400000000000000000000044131046102023000141300ustar 00000000000000use gpgme; use structopt; use gpgme::{Context, Protocol}; use std::{ error::Error, fs::File, io::{self, prelude::*}, path::PathBuf, }; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long)] /// Use the CMS protocol cms: bool, #[structopt(long)] #[structopt(long, conflicts_with = "normal")] /// Create a detached signature detach: bool, #[structopt(long, conflicts_with = "normal", conflicts_with = "detach")] /// Create a clear text signature clear: bool, #[structopt(long)] /// Key to use for signing. Default key is used otherwise key: Option, #[structopt(parse(from_os_str))] /// File to sign filename: PathBuf, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let proto = if args.cms { Protocol::Cms } else { Protocol::OpenPgp }; let mode = if args.detach { gpgme::SignMode::Detached } else if args.clear { gpgme::SignMode::Clear } else { gpgme::SignMode::Normal }; let mut ctx = Context::from_protocol(proto)?; ctx.set_armor(true); if let Some(key) = args.key { let key = ctx .get_secret_key(key) .map_err(|e| format!("unable to find signing key: {:?}", e))?; ctx.add_signer(&key) .map_err(|e| format!("add_signer() failed: {:?}", e))?; } let filename = &args.filename; let mut input = File::open(filename) .map_err(|e| format!("can't open file `{}': {:?}", filename.display(), e))?; let mut output = Vec::new(); let result = ctx .sign(mode, &mut input, &mut output) .map_err(|e| format!("signing failed {:?}", e))?; print_result(&result); println!("Begin Output:"); io::stdout().write_all(&output)?; println!("End Output."); Ok(()) } fn print_result(result: &gpgme::SigningResult) { for sig in result.new_signatures() { println!("Key fingerprint: {}", sig.fingerprint().unwrap_or("[none]")); println!("Signature type : {:?}", sig.mode()); println!("Public key algo: {}", sig.key_algorithm()); println!("Hash algo .....: {}", sig.hash_algorithm()); println!("Creation time .: {:?}", sig.creation_time()); } } gpgme-0.11.0/examples/swdb.rs000064400000000000000000000010111046102023000141160ustar 00000000000000extern crate gpgme; use structopt; use gpgme::{Context, Protocol}; use std::error::Error; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { name: Option, version: Option, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let mut ctx = Context::from_protocol(Protocol::GpgConf)?; let result = ctx .query_swdb(args.name, args.version) .map_err(|e| format!("query failed: {:?}", e))?; println!("{:#?}", result); Ok(()) } gpgme-0.11.0/examples/verify.rs000064400000000000000000000067111046102023000144770ustar 00000000000000use gpgme; use structopt; use gpgme::{Context, Protocol, SignatureSummary}; use std::{error::Error, fs::File, path::PathBuf}; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Cli { #[structopt(long)] /// Use the CMS protocol cms: bool, #[structopt(parse(from_os_str))] sigfile: PathBuf, #[structopt(parse(from_os_str))] filename: Option, } fn main() -> Result<(), Box> { let args = Cli::from_args(); let proto = if args.cms { Protocol::Cms } else { Protocol::OpenPgp }; let mut ctx = Context::from_protocol(proto)?; let sigfile = &args.sigfile; let signature = File::open(sigfile).map_err(|e| format!("can't open '{}': {:?}", sigfile.display(), e))?; let result = if let Some(filename) = args.filename.as_ref() { let signed = File::open(filename) .map_err(|e| format!("can't open '{}': {:?}", filename.display(), e))?; ctx.verify_detached(signature, signed) } else { ctx.verify_opaque(signature, &mut Vec::new()) }; print_result(&result.map_err(|e| format!("verification failed: {:?}", e))?); Ok(()) } fn print_summary(summary: SignatureSummary) { if summary.contains(SignatureSummary::VALID) { print!(" valid"); } if summary.contains(SignatureSummary::GREEN) { print!(" green"); } if summary.contains(SignatureSummary::RED) { print!(" red"); } if summary.contains(SignatureSummary::KEY_REVOKED) { print!(" revoked"); } if summary.contains(SignatureSummary::KEY_EXPIRED) { print!(" key-expired"); } if summary.contains(SignatureSummary::SIG_EXPIRED) { print!(" sig-expired"); } if summary.contains(SignatureSummary::KEY_MISSING) { print!(" key-missing"); } if summary.contains(SignatureSummary::CRL_MISSING) { print!(" crl-missing"); } if summary.contains(SignatureSummary::CRL_TOO_OLD) { print!(" crl-too-old"); } if summary.contains(SignatureSummary::BAD_POLICY) { print!(" bad-policy"); } if summary.contains(SignatureSummary::SYS_ERROR) { print!(" sys-error"); } } fn print_result(result: &gpgme::VerificationResult) { println!( "Original file name: {}", result.filename().unwrap_or("[none]") ); for (i, sig) in result.signatures().enumerate() { println!("Signature {}", i); println!(" status ....: {:?}", sig.status()); print!(" summary ...:"); print_summary(sig.summary()); println!(""); println!(" fingerprint: {}", sig.fingerprint().unwrap_or("[none]")); println!(" created ...: {:?}", sig.creation_time()); println!(" expires ...: {:?}", sig.expiration_time()); println!(" validity ..: {:?}", sig.validity()); println!(" val.reason : {:?}", sig.nonvalidity_reason()); println!(" pubkey algo: {}", sig.key_algorithm()); println!(" digest algo: {}", sig.hash_algorithm()); println!(" pka address: {}", sig.pka_address().unwrap_or("[none]")); println!(" pka trust .: {:?}", sig.pka_trust()); println!( " other flags: {}{}", if sig.is_wrong_key_usage() { " wrong-key-usage" } else { "" }, if sig.verified_by_chain() { " chain-model" } else { "" } ); } } gpgme-0.11.0/src/bin/pinentry.rs000064400000000000000000000006461046102023000145650ustar 00000000000000use std::{io, io::prelude::*}; #[allow(dead_code)] fn main() { println!("OK Your orders please"); let stdin = io::stdin(); let mut lines = stdin.lock().lines(); while let Some(Ok(cmd)) = lines.next() { match cmd.split(' ').nth(0) { Some("GETPIN") => { println!("D abc"); println!("OK"); } _ => println!("OK"), } } } gpgme-0.11.0/src/callbacks.rs000064400000000000000000000234111046102023000140570ustar 00000000000000#![allow(deprecated)] use std::{ ffi::CStr, io::{self, prelude::*}, panic::{self, UnwindSafe}, str::Utf8Error, thread, }; use ffi; use libc; use static_assertions::assert_obj_safe; use crate::{edit, utils::FdWriter, Data, Error, Result}; assert_obj_safe!(PassphraseProvider); assert_obj_safe!(ProgressReporter); assert_obj_safe!(StatusHandler); assert_obj_safe!(EditInteractor); assert_obj_safe!(Interactor); #[derive(Debug, Copy, Clone)] pub struct PassphraseRequest<'a> { uid_hint: Option<&'a CStr>, desc: Option<&'a CStr>, pub prev_attempt_failed: bool, } impl<'a> PassphraseRequest<'a> { pub fn user_id_hint(&self) -> Result<&'a str, Option> { self.uid_hint .map_or(Err(None), |s| s.to_str().map_err(Some)) } pub fn user_id_hint_raw(&self) -> Option<&'a CStr> { self.uid_hint } pub fn description(&self) -> Result<&'a str, Option> { self.desc.map_or(Err(None), |s| s.to_str().map_err(Some)) } pub fn description_raw(&self) -> Option<&'a CStr> { self.desc } } /// Upstream documentation: /// [`gpgme_passphrase_cb_t`](https://www.gnupg.org/documentation/manuals/gpgme/Passphrase-Callback.html#index-gpgme_005fpassphrase_005fcb_005ft) pub trait PassphraseProvider: UnwindSafe + Send { fn get_passphrase(&mut self, request: PassphraseRequest<'_>, out: &mut dyn Write) -> Result<()>; } impl PassphraseProvider for T where T: FnMut(PassphraseRequest<'_>, &mut dyn io::Write) -> Result<()>, { fn get_passphrase( &mut self, request: PassphraseRequest<'_>, out: &mut dyn Write, ) -> Result<()> { (*self)(request, out) } } #[derive(Debug, Copy, Clone)] pub struct ProgressInfo<'a> { what: Option<&'a CStr>, pub typ: i64, pub current: i64, pub total: i64, } impl<'a> ProgressInfo<'a> { pub fn what(&self) -> Result<&'a str, Option> { self.what.map_or(Err(None), |s| s.to_str().map_err(Some)) } pub fn what_raw(&self) -> Option<&'a CStr> { self.what } } /// Upstream documentation: /// [`gpgme_progress_cb_t`](https://www.gnupg.org/documentation/manuals/gpgme/Progress-Meter-Callback.html#index-gpgme_005fprogress_005fcb_005ft) pub trait ProgressReporter: UnwindSafe + Send { fn report(&mut self, info: ProgressInfo<'_>); } impl ProgressReporter for T where T: FnMut(ProgressInfo<'_>), { fn report(&mut self, info: ProgressInfo<'_>) { (*self)(info); } } /// Upstream documentation: /// [`gpgme_status_cb_t`](https://www.gnupg.org/documentation/manuals/gpgme/Status-Message-Callback.html#index-gpgme_005fstatus_005fcb_005ft) pub trait StatusHandler: UnwindSafe + Send { fn handle(&mut self, keyword: Option<&CStr>, args: Option<&CStr>) -> Result<()>; } impl StatusHandler for T where T: FnMut(Option<&CStr>, Option<&CStr>) -> Result<()>, { fn handle(&mut self, keyword: Option<&CStr>, args: Option<&CStr>) -> Result<()> { (*self)(keyword, args) } } #[derive(Debug)] pub struct EditInteractionStatus<'a> { pub code: edit::StatusCode, args: Option<&'a CStr>, pub response: &'a mut Data<'a>, } impl<'a> EditInteractionStatus<'a> { pub fn args(&self) -> Result<&'a str, Option> { match self.args { Some(s) => s.to_str().map_err(Some), None => Err(None), } } pub fn args_raw(&self) -> Option<&'a CStr> { self.args } } /// Upstream documentation: /// [`gpgme_edit_cb_t`](https://www.gnupg.org/documentation/manuals/gpgme/Deprecated-Functions.html#index-gpgme_005fedit_005fcb_005ft) #[deprecated(since = "0.9.2")] pub trait EditInteractor: UnwindSafe + Send { fn interact( &mut self, status: EditInteractionStatus<'_>, out: Option<&mut dyn Write>, ) -> Result<()>; } #[derive(Debug)] pub struct InteractionStatus<'a> { keyword: Option<&'a CStr>, args: Option<&'a CStr>, pub response: &'a mut Data<'a>, } impl<'a> InteractionStatus<'a> { pub fn keyword(&self) -> Result<&'a str, Option> { self.keyword.map_or(Err(None), |s| s.to_str().map_err(Some)) } pub fn keyword_raw(&self) -> Option<&'a CStr> { self.keyword } pub fn args(&self) -> Result<&'a str, Option> { self.args.map_or(Err(None), |s| s.to_str().map_err(Some)) } pub fn args_raw(&self) -> Option<&'a CStr> { self.args } } /// Upstream documentation: /// [`gpgme_interact_cb_t`](https://www.gnupg.org/documentation/manuals/gpgme/Advanced-Key-Editing.html#index-gpgme_005finteract_005fcb_005ft) pub trait Interactor: UnwindSafe + Send { fn interact( &mut self, status: InteractionStatus<'_>, out: Option<&mut dyn Write>, ) -> Result<(), Error>; } pub(crate) struct Hook(Option>); impl Drop for Hook { fn drop(&mut self) { if let Some(Err(err)) = self.0.take() { panic::resume_unwind(err); } } } impl From for Hook { fn from(hook: T) -> Self { Self(Some(Ok(hook))) } } impl Hook { fn update(&mut self, f: F) -> ffi::gpgme_error_t where F: UnwindSafe + FnOnce(&mut T) -> Result<()>, { let mut provider = match self.0.take() { Some(Ok(p)) => p, other => { self.0 = other; return ffi::GPG_ERR_GENERAL; } }; match panic::catch_unwind(move || { let result = f(&mut provider); (provider, result) }) { Ok((provider, result)) => { self.0 = Some(Ok(provider)); result.err().map_or(0, |err| err.raw()) } Err(err) => { self.0 = Some(Err(err)); ffi::GPG_ERR_GENERAL } } } } pub(crate) struct PassphraseCbGuard { pub ctx: ffi::gpgme_ctx_t, pub old: (ffi::gpgme_passphrase_cb_t, *mut libc::c_void), } impl Drop for PassphraseCbGuard { fn drop(&mut self) { unsafe { ffi::gpgme_set_passphrase_cb(self.ctx, self.old.0, self.old.1); } } } pub(crate) struct ProgressCbGuard { pub ctx: ffi::gpgme_ctx_t, pub old: (ffi::gpgme_progress_cb_t, *mut libc::c_void), } impl Drop for ProgressCbGuard { fn drop(&mut self) { unsafe { ffi::gpgme_set_progress_cb(self.ctx, self.old.0, self.old.1); } } } pub(crate) struct StatusCbGuard { pub ctx: ffi::gpgme_ctx_t, pub old: (ffi::gpgme_status_cb_t, *mut libc::c_void), } impl Drop for StatusCbGuard { fn drop(&mut self) { unsafe { ffi::gpgme_set_status_cb(self.ctx, self.old.0, self.old.1); } } } pub(crate) struct InteractorHook<'a, I> { pub inner: Hook, pub response: *mut Data<'a>, } pub(crate) unsafe extern "C" fn passphrase_cb( hook: *mut libc::c_void, uid_hint: *const libc::c_char, info: *const libc::c_char, was_bad: libc::c_int, fd: libc::c_int, ) -> ffi::gpgme_error_t { (*hook.cast::>()).update(move |h| { let info = PassphraseRequest { uid_hint: uid_hint.as_ref().map(|s| CStr::from_ptr(s)), desc: info.as_ref().map(|s| CStr::from_ptr(s)), prev_attempt_failed: was_bad != 0, }; let mut writer = FdWriter::new(fd); h.get_passphrase(info, &mut writer) .and_then(|_| writer.write_all(b"\n").map_err(Error::from)) }) } pub(crate) unsafe extern "C" fn progress_cb( hook: *mut libc::c_void, what: *const libc::c_char, typ: libc::c_int, current: libc::c_int, total: libc::c_int, ) { (*hook.cast::>()).update(move |h| { let info = ProgressInfo { what: what.as_ref().map(|s| CStr::from_ptr(s)), typ: typ.into(), current: current.into(), total: total.into(), }; h.report(info); Ok(()) }); } pub(crate) unsafe extern "C" fn status_cb( hook: *mut libc::c_void, keyword: *const libc::c_char, args: *const libc::c_char, ) -> ffi::gpgme_error_t { (*hook.cast::>()).update(move |h| { let keyword = keyword.as_ref().map(|s| CStr::from_ptr(s)); let args = args.as_ref().map(|s| CStr::from_ptr(s)); h.handle(args, keyword) }) } pub(crate) unsafe extern "C" fn edit_cb( hook: *mut libc::c_void, status: ffi::gpgme_status_code_t, args: *const libc::c_char, fd: libc::c_int, ) -> ffi::gpgme_error_t { let hook = &mut *hook.cast::>(); let response = hook.response; hook.inner.update(move |h| { let status = EditInteractionStatus { code: edit::StatusCode::from_raw(status), args: args.as_ref().map(|s| CStr::from_ptr(s)), response: &mut *response, }; if fd < 0 { h.interact(status, None) } else { h.interact(status, Some(&mut FdWriter::new(fd))) } }) } pub(crate) unsafe extern "C" fn interact_cb( hook: *mut libc::c_void, keyword: *const libc::c_char, args: *const libc::c_char, fd: libc::c_int, ) -> ffi::gpgme_error_t { let hook = &mut *hook.cast::>(); let response = hook.response; hook.inner.update(move |h| { let status = InteractionStatus { keyword: keyword.as_ref().map(|s| CStr::from_ptr(s)), args: args.as_ref().map(|s| CStr::from_ptr(s)), response: &mut *response, }; if fd < 0 { h.interact(status, None) } else { h.interact(status, Some(&mut FdWriter::new(fd))) } }) } gpgme-0.11.0/src/context.rs000064400000000000000000002235351046102023000136350ustar 00000000000000use std::{ borrow::BorrowMut, ffi::CStr, fmt, iter::FusedIterator, mem::ManuallyDrop, ops::{Deref, DerefMut}, ptr, str::Utf8Error, time::Duration, }; use conv::{UnwrapOrSaturate, ValueInto}; use ffi; use libc; #[allow(deprecated)] use crate::{ callbacks, edit, engine::EngineInfo, error::return_err, notation::SignatureNotations, results, utils::{CStrArgument, SmallVec}, Data, EditInteractor, Error, ExportMode, Interactor, IntoData, Key, KeyListMode, NonNull, PassphraseProvider, ProgressReporter, Protocol, Result, SignMode, StatusHandler, }; /// A context for cryptographic operations. /// /// Upstream documentation: /// [`gpgme_ctx_t`](https://www.gnupg.org/documentation/manuals/gpgme/Contexts.html#Contexts) #[must_use] pub struct Context(NonNull); impl Drop for Context { #[inline] fn drop(&mut self) { unsafe { ffi::gpgme_release(self.as_raw()) } } } impl Context { impl_wrapper!(ffi::gpgme_ctx_t); fn new() -> Result { crate::init(); unsafe { let mut ctx = ptr::null_mut(); return_err!(ffi::gpgme_new(&mut ctx)); Ok(Context::from_raw(ctx)) } } /// Creates a new context and initializes it to work with the specified protocol. /// /// Upstream documentation: [`gpgme_new`] and [`gpgme_set_protocol`] /// /// [`gpgme_new`]: https://www.gnupg.org/documentation/manuals/gpgme/Creating-Contexts.html#Creating-Contexts /// [`gpgme_set_protocol`]: https://www.gnupg.org/documentation/manuals/gpgme/Protocol-Selection.html#index-gpgme_005fset_005fprotocol #[inline] pub fn from_protocol(proto: Protocol) -> Result { let ctx = Context::new()?; unsafe { return_err!(ffi::gpgme_set_protocol(ctx.as_raw(), proto.raw())); } Ok(ctx) } /// Upstream documentation: /// [`gpgme_get_protocol`](https://www.gnupg.org/documentation/manuals/gpgme/Protocol-Selection.html#index-gpgme_005fget_005fprotocol) #[inline] pub fn protocol(&self) -> Protocol { unsafe { Protocol::from_raw(ffi::gpgme_get_protocol(self.as_raw())) } } /// Upstream documentation: /// [`gpgme_get_armor`](https://www.gnupg.org/documentation/manuals/gpgme/ASCII-Armor.html#index-gpgme_005fget_005farmor) #[inline] pub fn armor(&self) -> bool { unsafe { ffi::gpgme_get_armor(self.as_raw()) != 0 } } /// Upstream documentation: /// [`gpgme_set_armor`](https://www.gnupg.org/documentation/manuals/gpgme/ASCII-Armor.html#index-gpgme_005fset_005farmor) #[inline] pub fn set_armor(&mut self, enabled: bool) { unsafe { ffi::gpgme_set_armor(self.as_raw(), if enabled { 1 } else { 0 }); } } /// Upstream documentation: /// [`gpgme_get_textmode`](https://www.gnupg.org/documentation/manuals/gpgme/Text-Mode.html#index-gpgme_005fget_005ftextmode) #[inline] pub fn text_mode(&self) -> bool { unsafe { ffi::gpgme_get_textmode(self.as_raw()) != 0 } } /// Upstream documentation: /// [`gpgme_set_textmode`](https://www.gnupg.org/documentation/manuals/gpgme/Text-Mode.html#index-gpgme_005fset_005ftextmode) #[inline] pub fn set_text_mode(&mut self, enabled: bool) { unsafe { ffi::gpgme_set_textmode(self.as_raw(), if enabled { 1 } else { 0 }); } } /// Upstream documentation: /// [`gpgme_get_offline`](https://www.gnupg.org/documentation/manuals/gpgme/Offline-Mode.html#index-gpgme_005fget_005foffline) #[inline] pub fn offline(&self) -> bool { unsafe { ffi::gpgme_get_offline(self.as_raw()) != 0 } } /// Upstream documentation: /// [`gpgme_set_offline`](https://www.gnupg.org/documentation/manuals/gpgme/Offline-Mode.html#index-gpgme_005fset_005foffline) #[inline] pub fn set_offline(&mut self, enabled: bool) { unsafe { ffi::gpgme_set_offline(self.as_raw(), if enabled { 1 } else { 0 }); } } /// Upstream documentation: /// [`gpgme_get_ctx_flag`](https://www.gnupg.org/documentation/manuals/gpgme/Context-Flags.html#index-gpgme_005fget_005fctx_005fflag) #[inline] pub fn get_flag(&self, name: impl CStrArgument) -> Result<&str, Option> { self.get_flag_raw(name) .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Upstream documentation: /// [`gpgme_get_ctx_flag`](https://www.gnupg.org/documentation/manuals/gpgme/Context-Flags.html#index-gpgme_005fget_005fctx_005fflag) #[inline] pub fn get_flag_raw(&self, name: impl CStrArgument) -> Option<&CStr> { let name = name.into_cstr(); unsafe { ffi::gpgme_get_ctx_flag(self.as_raw(), name.as_ref().as_ptr()) .as_ref() .map(|s| CStr::from_ptr(s)) } } /// Upstream documentation: /// [`gpgme_set_ctx_flag`](https://www.gnupg.org/documentation/manuals/gpgme/Context-Flags.html#index-gpgme_005fset_005fctx_005fflag) #[inline] pub fn set_flag(&mut self, name: impl CStrArgument, value: impl CStrArgument) -> Result<()> { let name = name.into_cstr(); let value = value.into_cstr(); unsafe { return_err!(ffi::gpgme_set_ctx_flag( self.as_raw(), name.as_ref().as_ptr(), value.as_ref().as_ptr(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_ctx_get_engine_info`](https://www.gnupg.org/documentation/manuals/gpgme/Crypto-Engine.html#index-gpgme_005fctx_005fget_005fengine_005finfo) #[inline] pub fn engine_info(&self) -> EngineInfo<'_> { unsafe { EngineInfo::from_raw(ffi::gpgme_ctx_get_engine_info(self.as_raw())) } } #[inline] pub fn set_engine_path(&mut self, path: impl CStrArgument) -> Result<()> { let path = path.into_cstr(); let home_dir = self .engine_info() .home_dir_raw() .map_or(ptr::null(), CStr::as_ptr); unsafe { return_err!(ffi::gpgme_ctx_set_engine_info( self.as_raw(), self.protocol().raw(), path.as_ref().as_ptr(), home_dir, )); } Ok(()) } #[inline] pub fn set_engine_home_dir(&mut self, home_dir: impl CStrArgument) -> Result<()> { let path = self .engine_info() .path_raw() .map_or(ptr::null(), CStr::as_ptr); let home_dir = home_dir.into_cstr(); unsafe { return_err!(ffi::gpgme_ctx_set_engine_info( self.as_raw(), self.protocol().raw(), path, home_dir.as_ref().as_ptr(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_ctx_set_engine_info`](https://www.gnupg.org/documentation/manuals/gpgme/Crypto-Engine.html#index-gpgme_005fctx_005fset_005fengine_005finfo) #[inline] pub fn set_engine_info( &mut self, path: Option, home_dir: Option, ) -> Result<()> { let path = path.map(CStrArgument::into_cstr); let home_dir = home_dir.map(CStrArgument::into_cstr); unsafe { let path = path.as_ref().map_or(ptr::null(), |s| s.as_ref().as_ptr()); let home_dir = home_dir .as_ref() .map_or(ptr::null(), |s| s.as_ref().as_ptr()); return_err!(ffi::gpgme_ctx_set_engine_info( self.as_raw(), self.protocol().raw(), path, home_dir, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_get_pinentry_mode`](https://www.gnupg.org/documentation/manuals/gpgme/Pinentry-Mode.html#index-gpgme_005fget_005fpinentry_005fmode) #[inline] pub fn pinentry_mode(&self) -> crate::PinentryMode { unsafe { crate::PinentryMode::from_raw(ffi::gpgme_get_pinentry_mode(self.as_raw())) } } /// Upstream documentation: /// [`gpgme_set_pinentry_mode`](https://www.gnupg.org/documentation/manuals/gpgme/Pinentry-Mode.html#index-gpgme_005fset_005fpinentry_005fmode) #[inline] pub fn set_pinentry_mode(&mut self, mode: crate::PinentryMode) -> Result<()> { if (mode != crate::PinentryMode::Default) && (self.protocol() == Protocol::OpenPgp) && !self.engine_info().check_version("2.1") { return Err(Error::NOT_SUPPORTED); } unsafe { return_err!(ffi::gpgme_set_pinentry_mode(self.as_raw(), mode.raw())); } Ok(()) } /// Upstream documentation: /// [`gpgme_set_passphrase_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Passphrase-Callback.html#index-gpgme_005fset_005fpassphrase_005fcb) #[inline] pub fn clear_passphrase_provider(&mut self) { unsafe { ffi::gpgme_set_passphrase_cb(self.as_raw(), None, ptr::null_mut()); } } /// Upstream documentation: /// [`gpgme_set_passphrase_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Passphrase-Callback.html#index-gpgme_005fset_005fpassphrase_005fcb) #[inline] pub fn set_passphrase_provider<'a, P>(self, provider: P) -> ContextWithCallbacks<'a> where P: PassphraseProvider + 'a, { let mut wrapper = ContextWithCallbacks::from(self); wrapper.set_passphrase_provider(provider); wrapper } /// Uses the specified provider to handle passphrase requests for the duration of the /// closure. /// /// Upstream documentation: /// [`gpgme_set_passphrase_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Passphrase-Callback.html#index-gpgme_005fset_005fpassphrase_005fcb) /// /// # Examples /// /// ```no_run /// use std::io::prelude::*; /// /// use gpgme::{Context, PassphraseRequest, Protocol}; /// /// let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; /// ctx.with_passphrase_provider(|_: PassphraseRequest, out: &mut dyn Write| { /// out.write_all(b"some passphrase")?; /// Ok(()) /// }, |mut ctx| { /// // Do something with ctx requiring a passphrase, for example decryption /// }); /// # Ok::<(), gpgme::Error>(()) /// ``` pub fn with_passphrase_provider( &mut self, provider: P, f: impl FnOnce(&mut Context) -> R, ) -> R where P: PassphraseProvider, { unsafe { let mut old = (None, ptr::null_mut()); ffi::gpgme_get_passphrase_cb(self.as_raw(), &mut old.0, &mut old.1); let mut hook = callbacks::Hook::from(provider); let _guard = callbacks::PassphraseCbGuard { ctx: self.as_raw(), old, }; ffi::gpgme_set_passphrase_cb( self.as_raw(), Some(callbacks::passphrase_cb::

), ptr::addr_of_mut!(hook).cast(), ); f(self) } } /// Upstream documentation: /// [`gpgme_set_progress_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Progress-Meter-Callback.html#index-gpgme_005fset_005fprogress_005fcb) #[inline] pub fn clear_progress_reporter(&mut self) { unsafe { ffi::gpgme_set_progress_cb(self.as_raw(), None, ptr::null_mut()); } } /// Upstream documentation: /// [`gpgme_set_progress_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Progress-Meter-Callback.html#index-gpgme_005fset_005fprogress_005fcb) #[inline] pub fn set_progress_reporter<'a, P>(self, reporter: P) -> ContextWithCallbacks<'a> where P: ProgressReporter + 'a, { let mut wrapper = ContextWithCallbacks::from(self); wrapper.set_progress_reporter(reporter); wrapper } /// Upstream documentation: /// [`gpgme_set_progress_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Progress-Meter-Callback.html#index-gpgme_005fset_005fprogress_005fcb) pub fn with_progress_reporter( &mut self, handler: H, f: impl FnOnce(&mut Context) -> R, ) -> R where H: ProgressReporter, { unsafe { let mut old = (None, ptr::null_mut()); ffi::gpgme_get_progress_cb(self.as_raw(), &mut old.0, &mut old.1); let mut hook = callbacks::Hook::from(handler); let _guard = callbacks::ProgressCbGuard { ctx: self.as_raw(), old, }; ffi::gpgme_set_progress_cb( self.as_raw(), Some(callbacks::progress_cb::), ptr::addr_of_mut!(hook).cast(), ); f(self) } } /// Upstream documentation: /// [`gpgme_set_status_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Status-Message-Callback.html#index-gpgme_005fset_005fstatus_005fcb) pub fn clear_status_handler(&mut self) { unsafe { ffi::gpgme_set_status_cb(self.as_raw(), None, ptr::null_mut()); } } /// Upstream documentation: /// [`gpgme_set_status_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Status-Message-Callback.html#index-gpgme_005fset_005fstatus_005fcb) #[inline] pub fn set_status_handler<'a, H>(self, handler: H) -> ContextWithCallbacks<'a> where H: StatusHandler + 'a, { let mut wrapper = ContextWithCallbacks::from(self); wrapper.set_status_handler(handler); wrapper } /// Upstream documentation: /// [`gpgme_set_status_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Status-Message-Callback.html#index-gpgme_005fset_005fstatus_005fcb) pub fn with_status_handler(&mut self, handler: H, f: impl FnOnce(&mut Context) -> R) -> R where H: StatusHandler, { unsafe { let mut old = (None, ptr::null_mut()); ffi::gpgme_get_status_cb(self.as_raw(), &mut old.0, &mut old.1); let mut hook = callbacks::Hook::from(handler); let _guard = callbacks::StatusCbGuard { ctx: self.as_raw(), old, }; ffi::gpgme_set_status_cb( self.as_raw(), Some(callbacks::status_cb::), ptr::addr_of_mut!(hook).cast(), ); f(self) } } /// Upstream documentation: /// [`gpgme_get_keylist_mode`](https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html#index-gpgme_005fget_005fkeylist_005fmode) #[inline] pub fn key_list_mode(&self) -> KeyListMode { unsafe { crate::KeyListMode::from_bits_truncate(ffi::gpgme_get_keylist_mode(self.as_raw())) } } /// Adds all flags set in the provided key listing mode to the `Context`'s current mode. /// /// Upstream documentation: /// [`gpgme_set_keylist_mode`](https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html#index-gpgme_005fset_005fkeylist_005fmode) #[inline] pub fn add_key_list_mode(&mut self, mask: KeyListMode) -> Result<()> { unsafe { let old = ffi::gpgme_get_keylist_mode(self.as_raw()); return_err!(ffi::gpgme_set_keylist_mode( self.as_raw(), mask.bits() | old, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_set_keylist_mode`](https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html#index-gpgme_005fset_005fkeylist_005fmode) #[inline] pub fn set_key_list_mode(&mut self, mode: KeyListMode) -> Result<()> { unsafe { return_err!(ffi::gpgme_set_keylist_mode(self.as_raw(), mode.bits())); } Ok(()) } /// Returns an iterator over all public keys available in the keyring. #[inline] pub fn keys(&mut self) -> Result> { self.find_keys(None::) } /// Returns an iterator over all secret keys available in the keyring. #[inline] pub fn secret_keys(&mut self) -> Result> { self.find_secret_keys(None::) } /// Returns an updated version of the provided key. #[inline] pub fn refresh_key(&mut self, key: &Key) -> Result { let fpr = key.fingerprint_raw().ok_or(Error::AMBIGUOUS_NAME)?; if key.has_secret() { if let r @ Ok(_) = self.get_secret_key(fpr) { return r; } } self.get_key(fpr) } /// Returns the public key with the specified fingerprint, if such a key can /// be found. Otherwise, an error is returned. /// /// Upstream documentation: /// [`gpgme_get_key`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fget_005fkey) #[inline] pub fn get_key(&mut self, fpr: impl CStrArgument) -> Result { let fingerprint = fpr.into_cstr(); unsafe { let mut key = ptr::null_mut(); return_err!(ffi::gpgme_get_key( self.as_raw(), fingerprint.as_ref().as_ptr(), &mut key, 0, )); Ok(Key::from_raw(key)) } } /// Returns the secret key with the specified fingerprint, if such a key can /// be found. Otherwise, an error is returned. /// /// Upstream documentation: /// [`gpgme_get_key`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fget_005fkey) #[inline] pub fn get_secret_key(&mut self, fpr: impl CStrArgument) -> Result { let fingerprint = fpr.into_cstr(); unsafe { let mut key = ptr::null_mut(); return_err!(ffi::gpgme_get_key( self.as_raw(), fingerprint.as_ref().as_ptr(), &mut key, 1, )); Ok(Key::from_raw(key)) } } #[inline] pub fn locate_key(&mut self, email: impl CStrArgument) -> Result { self.add_key_list_mode(KeyListMode::LOCATE)?; self.find_keys(Some(email))? .next() .unwrap_or(Err(Error::NOT_FOUND)) } /// Returns an iterator over a list of all public keys matching one or more of the /// specified patterns. /// /// Upstream documentation: /// [`gpgme_op_keylist_ext_start`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fop_005fkeylist_005fext_005fstart) #[inline] pub fn find_keys(&mut self, patterns: I) -> Result> where I: IntoIterator, I::Item: CStrArgument, { self.search_keys(patterns, false) } /// Returns an iterator over a list of all secret keys matching one or more of the /// specified patterns. /// /// Upstream documentation: /// [`gpgme_op_keylist_ext_start`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fop_005fkeylist_005fext_005fstart) #[inline] pub fn find_secret_keys(&mut self, patterns: I) -> Result> where I: IntoIterator, I::Item: CStrArgument, { self.search_keys(patterns, true) } fn search_keys(&mut self, patterns: I, secret_only: bool) -> Result> where I: IntoIterator, I::Item: CStrArgument, { let patterns: SmallVec<_> = patterns.into_iter().map(|s| s.into_cstr()).collect(); let mut patterns: SmallVec<_> = patterns.iter().map(|s| s.as_ref().as_ptr()).collect(); let ptr = if !patterns.is_empty() { patterns.push(ptr::null()); patterns.as_mut_ptr() } else { ptr::null_mut() }; unsafe { return_err!(ffi::gpgme_op_keylist_ext_start( self.as_raw(), ptr, if secret_only { 1 } else { 0 }, 0, )); } Ok(Keys { ctx: self, _src: (), }) } /// Returns an iterator over the keys encoded in the specified source. /// /// Upstream documentation: /// [`gpgme_op_keylist_from_data_start`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fop_005fkeylist_005ffrom_005fdata_005fstart) /// /// # Examples /// /// ```no_run /// use gpgme::{Context, Data, Protocol}; /// /// let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; /// let mut keyring = Data::load("somefile")?; /// for key in ctx.read_keys(&mut keyring)? { /// println!("{:?}", key); /// } /// # Ok::<(), gpgme::Error>(()) /// ``` #[inline] pub fn read_keys<'s, 'd, D>(&'s mut self, src: D) -> Result> where D: IntoData<'d>, 's: 'd, { Keys::from_data(self, src) } /// Upstream documentation: /// [`gpgme_op_genkey`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fgenkey) #[inline] pub fn generate_key<'d1, 'd2, D1, D2>( &mut self, params: impl CStrArgument, public: Option, secret: Option, ) -> Result where D1: IntoData<'d1>, D2: IntoData<'d2>, { let params = params.into_cstr(); let mut public = public.map_or(Ok(None), |d| d.into_data().map(Some))?; let mut secret = secret.map_or(Ok(None), |d| d.into_data().map(Some))?; unsafe { return_err!(ffi::gpgme_op_genkey( self.as_raw(), params.as_ref().as_ptr(), public .as_mut() .map_or(ptr::null_mut(), |d| d.borrow_mut().as_raw()), secret .as_mut() .map_or(ptr::null_mut(), |d| d.borrow_mut().as_raw()), )); } Ok(self.get_result().unwrap()) } /// Creates a new OpenPGP key. /// /// Upstream documentation: /// [`gpgme_op_createkey`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fcreatekey) /// /// # Examples /// /// ```no_run /// use gpgme::{Context, Data, Protocol}; /// /// let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; /// let result = ctx.create_key("Example User ", "default", Default::default())?; /// println!("Key Fingerprint: {}", result.fingerprint().unwrap()); /// # Ok::<(), gpgme::Error>(()) /// ``` #[inline] pub fn create_key( &mut self, userid: impl CStrArgument, algo: impl CStrArgument, expires: Duration, ) -> Result { self.create_key_with_flags(userid, algo, expires, crate::CreateKeyFlags::empty()) } /// Upstream documentation: /// [`gpgme_op_createkey`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fcreatekey) #[inline] pub fn create_key_with_flags( &mut self, userid: impl CStrArgument, algo: impl CStrArgument, expires: Duration, flags: crate::CreateKeyFlags, ) -> Result { let userid = userid.into_cstr(); let algo = algo.into_cstr(); let expires = expires.as_secs().value_into().unwrap_or_saturate(); unsafe { return_err!(ffi::gpgme_op_createkey( self.as_raw(), userid.as_ref().as_ptr(), algo.as_ref().as_ptr(), 0, expires, ptr::null_mut(), flags.bits(), )); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_createsubkey`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fcreatesubkey) #[inline] pub fn create_subkey( &mut self, key: &Key, algo: impl CStrArgument, expires: Duration, ) -> Result { self.create_subkey_with_flags(key, algo, expires, crate::CreateKeyFlags::empty()) } /// Upstream documentation: /// [`gpgme_op_createsubkey`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fcreatesubkey) #[inline] pub fn create_subkey_with_flags( &mut self, key: &Key, algo: impl CStrArgument, expires: Duration, flags: crate::CreateKeyFlags, ) -> Result { let algo = algo.into_cstr(); let expires = expires.as_secs().value_into().unwrap_or_saturate(); unsafe { return_err!(ffi::gpgme_op_createsubkey( self.as_raw(), key.as_raw(), algo.as_ref().as_ptr(), 0, expires, flags.bits(), )); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_setexpire`](https://www.gnupg.org/documentation/manuals/gpgme/Manipulating-Keys.html#index-gpgme_005fop_005fsetexpire) #[cfg(feature = "v1_15")] pub fn set_expire_all(&mut self, key: &Key, expires: Duration) -> Result<()> { let expires = expires.as_secs().value_into().unwrap_or_saturate(); unsafe { return_err!(ffi::gpgme_op_setexpire( self.as_raw(), key.as_raw(), expires, b"*\0".into_cstr().as_ref().as_ptr(), 0, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_setexpire`](https://www.gnupg.org/documentation/manuals/gpgme/Manipulating-Keys.html#index-gpgme_005fop_005fsetexpire) #[cfg(feature = "v1_15")] pub fn set_expire(&mut self, key: &Key, expires: Duration, subkeys: I) -> Result<()> where I: IntoIterator, I::Item: CStrArgument, { let expires = expires.as_secs().value_into().unwrap_or_saturate(); self::with_joined_cstr(subkeys, |subkeys, _| { unsafe { return_err!(ffi::gpgme_op_setexpire( self.as_raw(), key.as_raw(), expires, subkeys.map_or(ptr::null(), |subkeys| subkeys.as_ptr()), 0, )); } Ok(()) }) } /// Upstream documentation: /// [`gpgme_op_adduid`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fadduid) #[inline] pub fn add_uid(&mut self, key: &Key, userid: impl CStrArgument) -> Result<()> { let userid = userid.into_cstr(); unsafe { return_err!(ffi::gpgme_op_adduid( self.as_raw(), key.as_raw(), userid.as_ref().as_ptr(), 0, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_revuid`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005frevuid) #[inline] pub fn revoke_uid(&mut self, key: &Key, userid: impl CStrArgument) -> Result<()> { let userid = userid.into_cstr(); unsafe { return_err!(ffi::gpgme_op_revuid( self.as_raw(), key.as_raw(), userid.as_ref().as_ptr(), 0, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_set_uid_flag`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fset_005fui_005fflag) #[inline] pub fn set_uid_flag( &mut self, key: &Key, userid: impl CStrArgument, name: impl CStrArgument, value: Option, ) -> Result<()> { let userid = userid.into_cstr(); let name = name.into_cstr(); let value = value.map(CStrArgument::into_cstr); unsafe { return_err!(ffi::gpgme_op_set_uid_flag( self.as_raw(), key.as_raw(), userid.as_ref().as_ptr(), name.as_ref().as_ptr(), value.as_ref().map_or(ptr::null(), |s| s.as_ref().as_ptr()), )); } Ok(()) } #[inline] pub fn set_primary_uid(&mut self, key: &Key, userid: impl CStrArgument) -> Result<()> { self.set_uid_flag(key, userid, "primary\0", None::) } /// Signs the given key with the default signing key, or the keys specified via /// [`add_signer`]. /// /// Upstream documentation: /// [`gpgme_op_keysign`](https://www.gnupg.org/documentation/manuals/gpgme/Signing-Keys.html#index-gpgme_005fop_005fkeysign) /// /// [`add_signer`]: struct.Context.html#method.add_signer #[inline] pub fn sign_key(&mut self, key: &Key, userids: I, expires: Duration) -> Result<()> where I: IntoIterator, I::Item: CStrArgument, { self.sign_key_with_flags(key, userids, expires, crate::KeySigningFlags::empty()) } /// Signs the given key with the default signing key, or the keys specified via /// [`add_signer`]. /// /// Upstream documentation: /// [`gpgme_op_keysign`](https://www.gnupg.org/documentation/manuals/gpgme/Signing-Keys.html#index-gpgme_005fop_005fkeysign) /// /// [`add_signer`]: struct.Context.html#method.add_signer pub fn sign_key_with_flags( &mut self, key: &Key, userids: I, expires: Duration, mut flags: crate::KeySigningFlags, ) -> Result<()> where I: IntoIterator, I::Item: CStrArgument, { let expires = expires.as_secs().value_into().unwrap_or_saturate(); self::with_joined_cstr(userids, |userids, count| { if count > 1 { flags |= crate::KeySigningFlags::LFSEP; } unsafe { return_err!(ffi::gpgme_op_keysign( self.as_raw(), key.as_raw(), userids.map_or(ptr::null(), |uid| uid.as_ptr()), expires, flags.bits(), )); } Ok(()) }) } /// Upstream documentation: /// [`gpgme_op_revsig`](https://www.gnupg.org/documentation/manuals/gpgme/Signing-Keys.html#index-gpgme_005fop_005frevsig) #[inline] #[cfg(feature = "v1_15")] pub fn revoke_signature(&mut self, key: &Key, signing_key: &Key, userids: I) -> Result<()> where I: IntoIterator, I::Item: CStrArgument, { self::with_joined_cstr(userids, |userids, count| { let flags = if count > 1 { ffi::GPGME_REVSIG_LFSEP } else { 0 }; unsafe { return_err!(ffi::gpgme_op_revsig( self.as_raw(), key.as_raw(), signing_key.as_raw(), userids.map_or(ptr::null(), |uid| uid.as_ptr()), flags, )); } Ok(()) }) } /// Upstream documentation: /// [`gpgme_op_tofu_policy`](https://www.gnupg.org/documentation/manuals/gpgme/Changing-TOFU-Data.html#index-gpgme_005fop_005ftofu_005fpolicy) #[inline] pub fn change_key_tofu_policy(&mut self, key: &Key, policy: crate::TofuPolicy) -> Result<()> { unsafe { return_err!(ffi::gpgme_op_tofu_policy( self.as_raw(), key.as_raw(), policy.raw(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_passwd`](https://www.gnupg.org/documentation/manuals/gpgme/Changing-Passphrases.html#index-gpgme_005fop_005fpasswd) #[inline] pub fn change_key_passphrase(&mut self, key: &Key) -> Result<()> { unsafe { return_err!(ffi::gpgme_op_passwd(self.as_raw(), key.as_raw(), 0)); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_edit`](https://www.gnupg.org/documentation/manuals/gpgme/Deprecated-Functions.html#index-gpgme_005fop_005fedit) #[inline] #[allow(deprecated)] #[deprecated(since = "0.9.2", note = "use `interact` instead")] pub fn edit_key<'a, E, D>(&mut self, key: &Key, interactor: E, data: D) -> Result<()> where E: EditInteractor, D: IntoData<'a>, { let mut data = data.into_data()?; let mut hook = callbacks::InteractorHook { inner: interactor.into(), response: data.borrow_mut(), }; unsafe { return_err!(ffi::gpgme_op_edit( self.as_raw(), key.as_raw(), Some(callbacks::edit_cb::), ptr::addr_of_mut!(hook).cast(), (*hook.response).as_raw(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_card_edit`](https://www.gnupg.org/documentation/manuals/gpgme/Deprecated-Functions.html#index-gpgme_005fop_005fcard_005fedit) #[inline] #[allow(deprecated)] #[deprecated(since = "0.9.2", note = "use `interact_with_card` instead")] pub fn edit_card_key<'a, E, D>(&mut self, key: &Key, interactor: E, data: D) -> Result<()> where E: EditInteractor, D: IntoData<'a>, { let mut data = data.into_data()?; let mut hook = callbacks::InteractorHook { inner: interactor.into(), response: data.borrow_mut(), }; unsafe { return_err!(ffi::gpgme_op_card_edit( self.as_raw(), key.as_raw(), Some(callbacks::edit_cb::), ptr::addr_of_mut!(hook).cast(), (*hook.response).as_raw(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_edit`](https://www.gnupg.org/documentation/manuals/gpgme/Deprecated-Functions.html#index-gpgme_005fop_005fedit) #[inline] #[deprecated(since = "0.9.2", note = "use `interact` instead")] pub fn edit_key_with<'a, D>( &mut self, key: &Key, editor: impl edit::Editor, data: D, ) -> Result<()> where D: IntoData<'a>, { #[allow(deprecated)] self.edit_key(key, edit::EditorWrapper::new(editor), data) } /// Upstream documentation: /// [`gpgme_op_card_edit`](https://www.gnupg.org/documentation/manuals/gpgme/Deprecated-Functions.html#index-gpgme_005fop_005fcard_005fedit) #[inline] #[deprecated(since = "0.9.2", note = "use `interact_with_card` instead")] pub fn edit_card_key_with<'a, D>( &mut self, key: &Key, editor: impl edit::Editor, data: D, ) -> Result<()> where D: IntoData<'a>, { #[allow(deprecated)] self.edit_card_key(key, edit::EditorWrapper::new(editor), data) } /// Upstream documentation: /// [`gpgme_op_interact`](https://www.gnupg.org/documentation/manuals/gpgme/Advanced-Key-Editing.html#index-gpgme_005fop_005finteract) #[inline] pub fn interact<'a, I, D>(&mut self, key: &Key, interactor: I, data: D) -> Result<()> where I: Interactor, D: IntoData<'a>, { self.interact_with_flags(Some(key), interactor, data, crate::InteractFlags::empty()) } /// Upstream documentation: /// [`gpgme_op_interact`](https://www.gnupg.org/documentation/manuals/gpgme/Advanced-Key-Editing.html#index-gpgme_005fop_005finteract) #[inline] pub fn interact_with_card<'a, I, D>(&mut self, key: &Key, interactor: I, data: D) -> Result<()> where I: Interactor, D: IntoData<'a>, { self.interact_with_flags(Some(key), interactor, data, crate::InteractFlags::CARD) } /// Upstream documentation: /// [`gpgme_op_interact`](https://www.gnupg.org/documentation/manuals/gpgme/Advanced-Key-Editing.html#index-gpgme_005fop_005finteract) #[inline] pub fn interact_with_flags<'a, I, D>( &mut self, key: Option<&Key>, interactor: I, data: D, flags: crate::InteractFlags, ) -> Result<()> where I: Interactor, D: IntoData<'a>, { let mut data = data.into_data()?; let mut hook = callbacks::InteractorHook { inner: interactor.into(), response: data.borrow_mut(), }; unsafe { return_err!(ffi::gpgme_op_interact( self.as_raw(), key.map_or(ptr::null_mut(), |k| k.as_raw()), flags.bits(), Some(callbacks::interact_cb::), ptr::addr_of_mut!(hook).cast(), (*hook.response).as_raw(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_delete`](https://www.gnupg.org/documentation/manuals/gpgme/Deleting-Keys.html#index-gpgme_005fop_005fdelete) #[inline] pub fn delete_key(&mut self, key: &Key) -> Result<()> { unsafe { return_err!(ffi::gpgme_op_delete(self.as_raw(), key.as_raw(), 0)); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_delete`](https://www.gnupg.org/documentation/manuals/gpgme/Deleting-Keys.html#index-gpgme_005fop_005fdelete) #[inline] pub fn delete_secret_key(&mut self, key: &Key) -> Result<()> { unsafe { return_err!(ffi::gpgme_op_delete(self.as_raw(), key.as_raw(), 1)); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_delete_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Deleting-Keys.html#index-gpgme_005fop_005fdelete_005fext) #[inline] pub fn delete_key_with_flags(&mut self, key: &Key, flags: crate::DeleteKeyFlags) -> Result<()> { unsafe { return_err!(ffi::gpgme_op_delete_ext( self.as_raw(), key.as_raw(), flags.bits() )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_import`](https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html#index-gpgme_005fop_005fimport) #[inline] pub fn import<'a, D>(&mut self, src: D) -> Result where D: IntoData<'a>, { let mut src = src.into_data()?; unsafe { return_err!(ffi::gpgme_op_import( self.as_raw(), src.borrow_mut().as_raw(), )); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_import_keys`](https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html#index-gpgme_005fop_005fimport_005fkeys) pub fn import_keys<'k, I>(&mut self, keys: I) -> Result where I: IntoIterator, { let mut ptrs: SmallVec<_> = keys.into_iter().map(Key::as_raw).collect(); let keys = if !ptrs.is_empty() { ptrs.push(ptr::null_mut()); ptrs.as_mut_ptr() } else { ptr::null_mut() }; unsafe { return_err!(ffi::gpgme_op_import_keys(self.as_raw(), keys)); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_receive_keys`](https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html#index-gpgme_005fop_005freceive_005fkeys) #[cfg(feature = "v1_17")] pub fn import_remote_keys(&mut self, uids: I) -> Result where I: IntoIterator, I::Item: CStrArgument, { let uids: SmallVec<_> = uids.into_iter().map(|s| s.into_cstr()).collect(); let uids: SmallVec<_> = uids .iter() .map(|s| s.as_ref().as_ptr()) .chain(Some(ptr::null())) .collect(); unsafe { return_err!(ffi::gpgme_op_receive_keys(self.as_raw(), uids.as_ptr())); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_export_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Exporting-Keys.html#index-gpgme_005fop_005fexport_005fext) #[inline] pub fn export_all_extern(&mut self, mode: ExportMode) -> Result<()> where I: IntoIterator, I::Item: CStrArgument, { self.export_(None::<&CStr>, mode | ExportMode::EXTERN, None) } /// Upstream documentation: /// [`gpgme_op_export_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Exporting-Keys.html#index-gpgme_005fop_005fexport_005fext) #[inline] pub fn export_extern(&mut self, patterns: I, mode: ExportMode) -> Result<()> where I: IntoIterator, I::Item: CStrArgument, { let patterns: SmallVec<_> = patterns.into_iter().map(|s| s.into_cstr()).collect(); self.export_(&patterns, mode | ExportMode::EXTERN, None) } /// Upstream documentation: /// [`gpgme_op_export_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Exporting-Keys.html#index-gpgme_005fop_005fexport_005fext) #[inline] pub fn export_all<'a, D>(&mut self, mode: ExportMode, dst: D) -> Result<()> where D: IntoData<'a>, { let mut dst = dst.into_data()?; self.export_(None::<&CStr>, mode, Some(dst.borrow_mut())) } /// Upstream documentation: /// [`gpgme_op_export_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Exporting-Keys.html#index-gpgme_005fop_005fexport_005fext) #[inline] pub fn export<'a, I, D>(&mut self, patterns: I, mode: ExportMode, dst: D) -> Result<()> where I: IntoIterator, I::Item: CStrArgument, D: IntoData<'a>, { let mut dst = dst.into_data()?; let patterns: SmallVec<_> = patterns.into_iter().map(|s| s.into_cstr()).collect(); self.export_(&patterns, mode, Some(dst.borrow_mut())) } fn export_( &mut self, patterns: I, mode: ExportMode, dst: Option<&mut Data<'_>>, ) -> Result<()> where I: IntoIterator, I::Item: AsRef, { let dst = dst.map_or(ptr::null_mut(), |d| d.as_raw()); let mut patterns: SmallVec<_> = patterns.into_iter().map(|s| s.as_ref().as_ptr()).collect(); let ptr = if !patterns.is_empty() { patterns.push(ptr::null()); patterns.as_mut_ptr() } else { ptr::null_mut() }; unsafe { return_err!(ffi::gpgme_op_export_ext( self.as_raw(), ptr, mode.bits(), dst, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_op_export_keys`](https://www.gnupg.org/documentation/manuals/gpgme/Exporting-Keys.html#index-gpgme_005fop_005fexport_005fkeys) #[inline] pub fn export_keys_extern<'k, I>(&mut self, keys: I, mode: ExportMode) -> Result<()> where I: IntoIterator, { self.export_keys_(keys, mode | ExportMode::EXTERN, None) } /// Upstream documentation: /// [`gpgme_op_export_keys`](https://www.gnupg.org/documentation/manuals/gpgme/Exporting-Keys.html#index-gpgme_005fop_005fexport_005fkeys) #[inline] pub fn export_keys<'k, 'a, I, D>(&mut self, keys: I, mode: ExportMode, dst: D) -> Result<()> where I: IntoIterator, D: IntoData<'a>, { let mut dst = dst.into_data()?; self.export_keys_(keys, mode, Some(dst.borrow_mut())) } fn export_keys_<'k, I>( &mut self, keys: I, mode: ExportMode, dst: Option<&mut Data<'_>>, ) -> Result<()> where I: IntoIterator, { let dst = dst.map_or(ptr::null_mut(), |d| d.as_raw()); let mut ptrs: SmallVec<_> = keys.into_iter().map(Key::as_raw).collect(); let keys = if !ptrs.is_empty() { ptrs.push(ptr::null_mut()); ptrs.as_mut_ptr() } else { ptr::null_mut() }; unsafe { return_err!(ffi::gpgme_op_export_keys( self.as_raw(), keys, mode.bits(), dst, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_set_sender`](https://www.gnupg.org/documentation/manuals/gpgme/Setting-the-Sender.html#index-gpgme_005fset_005fsender) #[inline] pub fn clear_sender(&mut self) -> Result<()> { unsafe { return_err!(ffi::gpgme_set_sender(self.as_raw(), ptr::null())); } Ok(()) } /// Upstream documentation: /// [`gpgme_set_sender`](https://www.gnupg.org/documentation/manuals/gpgme/Setting-the-Sender.html#index-gpgme_005fset_005fsender) #[inline] pub fn set_sender(&mut self, sender: impl CStrArgument) -> Result<()> { let sender = sender.into_cstr(); unsafe { return_err!(ffi::gpgme_set_sender( self.as_raw(), sender.as_ref().as_ptr(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_get_sender`](https://www.gnupg.org/documentation/manuals/gpgme/Setting-the-Sender.html#index-gpgme_005fget_005fsender) #[inline] pub fn sender(&self) -> Result<&str, Option> { self.sender_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Upstream documentation: /// [`gpgme_get_sender`](https://www.gnupg.org/documentation/manuals/gpgme/Setting-the-Sender.html#index-gpgme_005fget_005fsender) #[inline] pub fn sender_raw(&self) -> Option<&CStr> { unsafe { ffi::gpgme_get_sender(self.as_raw()) .as_ref() .map(|s| CStr::from_ptr(s)) } } /// Upstream documentation: /// [`gpgme_signers_clear`](https://www.gnupg.org/documentation/manuals/gpgme/Selecting-Signers.html#index-gpgme_005fsigners_005fclear) #[inline] pub fn clear_signers(&mut self) { unsafe { ffi::gpgme_signers_clear(self.as_raw()) } } /// Upstream documentation: /// [`gpgme_signers_add`](https://www.gnupg.org/documentation/manuals/gpgme/Selecting-Signers.html#index-gpgme_005fsigners_005fadd) #[inline] pub fn add_signer(&mut self, key: &Key) -> Result<()> { unsafe { return_err!(ffi::gpgme_signers_add(self.as_raw(), key.as_raw())); } Ok(()) } /// Upstream documentation: /// [`gpgme_signers_enum`](https://www.gnupg.org/documentation/manuals/gpgme/Selecting-Signers.html#index-gpgme_005fsigners_005fenum) #[inline] pub fn signers(&self) -> Signers<'_> { Signers { ctx: self, current: Some(0), } } /// Upstream documentation: /// [`gpgme_sig_notation_clear`](https://www.gnupg.org/documentation/manuals/gpgme/Signature-Notation-Data.html#index-gpgme_005fsig_005fnotation_005fclear) #[inline] pub fn clear_signature_notations(&mut self) { unsafe { ffi::gpgme_sig_notation_clear(self.as_raw()); } } /// Upstream documentation: /// [`gpgme_sig_notation_add`](https://www.gnupg.org/documentation/manuals/gpgme/Signature-Notation-Data.html#index-gpgme_005fsig_005fnotation_005fadd) #[inline] pub fn add_signature_notation( &mut self, name: impl CStrArgument, value: impl CStrArgument, flags: crate::SignatureNotationFlags, ) -> Result<()> { let name = name.into_cstr(); let value = value.into_cstr(); unsafe { return_err!(ffi::gpgme_sig_notation_add( self.as_raw(), name.as_ref().as_ptr(), value.as_ref().as_ptr(), flags.bits(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_sig_notation_add`](https://www.gnupg.org/documentation/manuals/gpgme/Signature-Notation-Data.html#index-gpgme_005fsig_005fnotation_005fadd) #[inline] pub fn add_signature_policy_url( &mut self, url: impl CStrArgument, critical: bool, ) -> Result<()> { let url = url.into_cstr(); unsafe { let critical = if critical { ffi::GPGME_SIG_NOTATION_CRITICAL } else { 0 }; return_err!(ffi::gpgme_sig_notation_add( self.as_raw(), ptr::null(), url.as_ref().as_ptr(), critical, )); } Ok(()) } /// Upstream documentation: /// [`gpgme_sig_notation_get`](https://www.gnupg.org/documentation/manuals/gpgme/Signature-Notation-Data.html#index-gpgme_005fsig_005fnotation_005fget) #[inline] pub fn signature_policy_url(&self) -> Result<&str, Option> { self.signature_policy_url_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Upstream documentation: /// [`gpgme_sig_notation_get`](https://www.gnupg.org/documentation/manuals/gpgme/Signature-Notation-Data.html#index-gpgme_005fsig_005fnotation_005fget) #[inline] pub fn signature_policy_url_raw(&self) -> Option<&CStr> { unsafe { let mut notation = ffi::gpgme_sig_notation_get(self.as_raw()); while !notation.is_null() { if (*notation).name.is_null() { return (*notation).value.as_ref().map(|s| CStr::from_ptr(s)); } notation = (*notation).next; } None } } /// Upstream documentation: /// [`gpgme_sig_notation_get`](https://www.gnupg.org/documentation/manuals/gpgme/Signature-Notation-Data.html#index-gpgme_005fsig_005fnotation_005fget) #[inline] pub fn signature_notations(&self) -> SignatureNotations<'_> { unsafe { SignatureNotations::from_list(ffi::gpgme_sig_notation_get(self.as_raw())) } } /// Creates a clear text signature. /// /// Upstream documentation: /// [`gpgme_op_sign`](https://www.gnupg.org/documentation/manuals/gpgme/Creating-a-Signature.html#index-gpgme_005fop_005fsign) #[inline] pub fn sign_clear<'p, 't, P, T>( &mut self, plaintext: P, signedtext: T, ) -> Result where P: IntoData<'p>, T: IntoData<'t>, { self.sign(SignMode::Clear, plaintext, signedtext) } /// Creates a detached signature. /// /// Upstream documentation: /// [`gpgme_op_sign`](https://www.gnupg.org/documentation/manuals/gpgme/Creating-a-Signature.html#index-gpgme_005fop_005fsign) #[inline] pub fn sign_detached<'p, 's, P, S>( &mut self, plaintext: P, signature: S, ) -> Result where P: IntoData<'p>, S: IntoData<'s>, { self.sign(SignMode::Detached, plaintext, signature) } /// Creates a normal signature. /// /// Upstream documentation: /// [`gpgme_op_sign`](https://www.gnupg.org/documentation/manuals/gpgme/Creating-a-Signature.html#index-gpgme_005fop_005fsign) #[inline] pub fn sign_normal<'p, 't, P, T>( &mut self, plaintext: P, signedtext: T, ) -> Result where P: IntoData<'p>, T: IntoData<'t>, { self.sign(SignMode::Normal, plaintext, signedtext) } /// Creates a signature for the text stored in the data object `plaintext` and writes it to the /// data object `signature`. /// /// The type of the signature created is determined by the ASCII armor (or, if that is not set, /// by the encoding specified for sig), the text mode attributes set for the context and the /// requested signature mode. /// /// Information about the results of the operation are returned in the `SigningResult` /// structure. /// /// Upstream documentation: /// [`gpgme_op_sign`](https://www.gnupg.org/documentation/manuals/gpgme/Creating-a-Signature.html#index-gpgme_005fop_005fsign) #[inline] pub fn sign<'p, 's, P, S>( &mut self, mode: crate::SignMode, plaintext: P, signature: S, ) -> Result where P: IntoData<'p>, S: IntoData<'s>, { let mut signature = signature.into_data()?; let mut plain = plaintext.into_data()?; unsafe { return_err!(ffi::gpgme_op_sign( self.as_raw(), plain.borrow_mut().as_raw(), signature.borrow_mut().as_raw(), mode.raw(), )); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_verify`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fop_005fverify) #[inline] pub fn verify_detached<'s, 't, S, T>( &mut self, signature: S, signedtext: T, ) -> Result where S: IntoData<'s>, T: IntoData<'t>, { let mut signature = signature.into_data()?; let mut signed = signedtext.into_data()?; self.verify(signature.borrow_mut(), Some(signed.borrow_mut()), None) } /// Upstream documentation: /// [`gpgme_op_verify`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fop_005fverify) #[inline] pub fn verify_opaque<'s, 'p, S, P>( &mut self, signedtext: S, plaintext: P, ) -> Result where S: IntoData<'s>, P: IntoData<'p>, { let mut signed = signedtext.into_data()?; let mut plain = plaintext.into_data()?; self.verify(signed.borrow_mut(), None, Some(plain.borrow_mut())) } fn verify( &mut self, signature: &mut Data<'_>, signedtext: Option<&mut Data<'_>>, plaintext: Option<&mut Data<'_>>, ) -> Result { unsafe { let signed = signedtext.map_or(ptr::null_mut(), |d| d.as_raw()); let plain = plaintext.map_or(ptr::null_mut(), |d| d.as_raw()); return_err!(ffi::gpgme_op_verify( self.as_raw(), signature.as_raw(), signed, plain, )); } Ok(self.get_result().unwrap()) } /// Encrypts a message for the specified recipients. /// /// Upstream documentation: /// [`gpgme_op_encrypt`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fop_005fencrypt) /// /// # Examples /// /// ```no_run /// use gpgme::{Context, Data, Protocol}; /// /// let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; /// let key = ctx.get_key("[some key fingerprint]")?; /// let (plaintext, mut ciphertext) = ("Hello, World!", Vec::new()); /// ctx.encrypt(Some(&key), plaintext, &mut ciphertext)?; /// # Ok::<(), gpgme::Error>(()) /// ``` #[inline] pub fn encrypt<'k, 'p, 'c, I, P, C>( &mut self, recp: I, plaintext: P, ciphertext: C, ) -> Result where I: IntoIterator, P: IntoData<'p>, C: IntoData<'c>, { self.encrypt_with_flags(recp, plaintext, ciphertext, crate::EncryptFlags::empty()) } /// Upstream documentation: /// [`gpgme_op_encrypt`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fop_005fencrypt) pub fn encrypt_with_flags<'k, 'p, 'c, I, P, C>( &mut self, recp: I, plaintext: P, ciphertext: C, flags: crate::EncryptFlags, ) -> Result where I: IntoIterator, P: IntoData<'p>, C: IntoData<'c>, { let mut plain = plaintext.into_data()?; let mut cipher = ciphertext.into_data()?; let mut ptrs: SmallVec<_> = recp.into_iter().map(Key::as_raw).collect(); let keys = if !ptrs.is_empty() { ptrs.push(ptr::null_mut()); ptrs.as_mut_ptr() } else { ptr::null_mut() }; unsafe { return_err!(ffi::gpgme_op_encrypt( self.as_raw(), keys, flags.bits(), plain.borrow_mut().as_raw(), cipher.borrow_mut().as_raw(), )); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_encrypt`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fop_005fencrypt) #[inline] pub fn encrypt_symmetric<'p, 'c, P, C>(&mut self, plaintext: P, ciphertext: C) -> Result<()> where P: IntoData<'p>, C: IntoData<'c>, { self.encrypt_symmetric_with_flags(plaintext, ciphertext, crate::EncryptFlags::empty()) } /// Upstream documentation: /// [`gpgme_op_encrypt`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fop_005fencrypt) #[inline] pub fn encrypt_symmetric_with_flags<'p, 'c, P, C>( &mut self, plaintext: P, ciphertext: C, flags: crate::EncryptFlags, ) -> Result<()> where P: IntoData<'p>, C: IntoData<'c>, { self.encrypt_with_flags(None, plaintext, ciphertext, flags)?; Ok(()) } /// Encrypts and signs a message for the specified recipients. /// /// Upstream documentation: /// [`gpgme_op_encrypt_sign`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fop_005fencrypt_005fsign) /// /// # Examples /// /// ```no_run /// use gpgme::{Context, Protocol}; /// /// let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; /// let key = ctx.get_key("[some key fingerprint]")?; /// let (plaintext, mut ciphertext) = ("Hello, World!", Vec::new()); /// ctx.sign_and_encrypt(Some(&key), plaintext, &mut ciphertext)?; /// # Ok::<(), gpgme::Error>(()) /// ``` #[inline] pub fn sign_and_encrypt<'k, 'p, 'c, I, P, C>( &mut self, recp: I, plaintext: P, ciphertext: C, ) -> Result<(results::EncryptionResult, results::SigningResult)> where I: IntoIterator, P: IntoData<'p>, C: IntoData<'c>, { self.sign_and_encrypt_with_flags(recp, plaintext, ciphertext, crate::EncryptFlags::empty()) } /// Upstream documentation: /// [`gpgme_op_encrypt_sign`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fop_005fencrypt_005fsign) pub fn sign_and_encrypt_with_flags<'k, 'p, 'c, I, P, C>( &mut self, recp: I, plaintext: P, ciphertext: C, flags: crate::EncryptFlags, ) -> Result<(results::EncryptionResult, results::SigningResult)> where I: IntoIterator, P: IntoData<'p>, C: IntoData<'c>, { let mut plain = plaintext.into_data()?; let mut cipher = ciphertext.into_data()?; let mut ptrs: SmallVec<_> = recp.into_iter().map(Key::as_raw).collect(); let keys = if !ptrs.is_empty() { ptrs.push(ptr::null_mut()); ptrs.as_mut_ptr() } else { ptr::null_mut() }; unsafe { return_err!(ffi::gpgme_op_encrypt_sign( self.as_raw(), keys, flags.bits(), plain.borrow_mut().as_raw(), cipher.borrow_mut().as_raw(), )) } Ok((self.get_result().unwrap(), self.get_result().unwrap())) } /// Decrypts a message. /// /// Upstream documentation: /// [`gpgme_op_decrypt`](https://www.gnupg.org/documentation/manuals/gpgme/Decrypt.html#index-gpgme_005fop_005fdecrypt) /// /// # Examples /// /// ```no_run /// use gpgme::{Context, Data, Protocol}; /// /// let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; /// let mut cipher = Data::load("some file")?; /// let mut plain = Vec::new(); /// ctx.decrypt(&mut cipher, &mut plain)?; /// # Ok::<(), gpgme::Error>(()) /// ``` #[inline] pub fn decrypt<'c, 'p, C, P>( &mut self, ciphertext: C, plaintext: P, ) -> Result where C: IntoData<'c>, P: IntoData<'p>, { let mut cipher = ciphertext.into_data()?; let mut plain = plaintext.into_data()?; unsafe { return_err!(ffi::gpgme_op_decrypt( self.as_raw(), cipher.borrow_mut().as_raw(), plain.borrow_mut().as_raw(), )); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_decrypt_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Decrypt.html#index-gpgme_005fop_005fdecrypt_005fext) #[inline] pub fn decrypt_with_flags<'c, 'p, C, P>( &mut self, ciphertext: C, plaintext: P, flags: crate::DecryptFlags, ) -> Result where C: IntoData<'c>, P: IntoData<'p>, { let mut cipher = ciphertext.into_data()?; let mut plain = plaintext.into_data()?; unsafe { return_err!(ffi::gpgme_op_decrypt_ext( self.as_raw(), flags.bits(), cipher.borrow_mut().as_raw(), plain.borrow_mut().as_raw(), )); } Ok(self.get_result().unwrap()) } /// Decrypts and verifies a message. /// /// Upstream documentation: /// [`gpgme_op_decrypt_verify`](https://www.gnupg.org/documentation/manuals/gpgme/Decrypt-and-Verify.html#index-gpgme_005fop_005fdecrypt_005fverify) /// /// # Examples /// /// ```no_run /// use gpgme::{Context, Data, Protocol}; /// /// let mut ctx = Context::from_protocol(Protocol::OpenPgp)?; /// let mut cipher = Data::load("some file")?; /// let mut plain = Vec::new(); /// ctx.decrypt_and_verify(&mut cipher, &mut plain)?; /// # Ok::<(), gpgme::Error>(()) /// ``` #[inline] pub fn decrypt_and_verify<'c, 'p, C, P>( &mut self, ciphertext: C, plaintext: P, ) -> Result<(results::DecryptionResult, results::VerificationResult)> where C: IntoData<'c>, P: IntoData<'p>, { let mut cipher = ciphertext.into_data()?; let mut plain = plaintext.into_data()?; unsafe { return_err!(ffi::gpgme_op_decrypt_verify( self.as_raw(), cipher.borrow_mut().as_raw(), plain.borrow_mut().as_raw(), )) } Ok((self.get_result().unwrap(), self.get_result().unwrap())) } /// Upstream documentation: /// [`gpgme_op_decrypt_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Decrypt.html#index-gpgme_005fop_005fdecrypt_005fext) #[inline] pub fn decrypt_and_verify_with_flags<'c, 'p, C, P>( &mut self, ciphertext: C, plaintext: P, flags: crate::DecryptFlags, ) -> Result<(results::DecryptionResult, results::VerificationResult)> where C: IntoData<'c>, P: IntoData<'p>, { self.decrypt_with_flags(ciphertext, plaintext, flags)?; Ok((self.get_result().unwrap(), self.get_result().unwrap())) } /// Upstream documentation: /// [`gpgme_op_query_swdb`](https://www.gnupg.org/documentation/manuals/gpgme/Checking-for-updates.html#index-gpgme_005fop_005fquery_005fswdb) #[inline] pub fn query_swdb( &mut self, name: Option, installed_ver: Option, ) -> Result { let name = name.map(|s| s.into_cstr()); let iversion = installed_ver.map(|s| s.into_cstr()); unsafe { let name = name.as_ref().map_or(ptr::null(), |s| s.as_ref().as_ptr()); let iversion = iversion .as_ref() .map_or(ptr::null(), |s| s.as_ref().as_ptr()); return_err!(ffi::gpgme_op_query_swdb(self.as_raw(), name, iversion, 0)); } Ok(self.get_result().unwrap()) } /// Upstream documentation: /// [`gpgme_op_getauditlog`](https://www.gnupg.org/documentation/manuals/gpgme/Additional-Logs.html#index-gpgme_005fop_005fgetauditlog) #[inline] pub fn get_audit_log<'a, D>(&mut self, dst: D, flags: crate::AuditLogFlags) -> Result<()> where D: IntoData<'a>, { let mut dst = dst.into_data()?; unsafe { return_err!(ffi::gpgme_op_getauditlog( self.as_raw(), dst.borrow_mut().as_raw(), flags.bits(), )); } Ok(()) } fn get_result(&self) -> Option { R::from_context(self) } } impl fmt::Debug for Context { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context") .field("raw", &self.as_raw()) .field("protocol", &self.protocol()) .field("armor", &self.armor()) .field("text_mode", &self.text_mode()) .field("engine", &self.engine_info()) .finish() } } /// An iterator type yielding `Key`s returned by a key listing operation. #[derive(Debug)] pub struct Keys<'ctx, D = ()> { ctx: &'ctx mut Context, _src: D, } impl Drop for Keys<'_, D> { #[inline] fn drop(&mut self) { unsafe { ffi::gpgme_op_keylist_end(self.ctx.as_raw()); } } } impl Keys<'_, D> { /// Upstream documentation: /// [`gpgme_op_keylist_end`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fop_005fkeylist_005fend) #[inline] pub fn finish(self) -> Result { let this = ManuallyDrop::new(self); unsafe { return_err!(ffi::gpgme_op_keylist_end(this.ctx.as_raw())); } Ok(this.ctx.get_result().unwrap()) } } impl<'ctx> Keys<'ctx, ()> { /// Upstream documentation: /// [`gpgme_op_keylist_from_data_start`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fop_005fkeylist_005ffrom_005fdata_005fstart) #[inline] pub fn from_data<'d, D>(ctx: &'ctx mut Context, src: D) -> Result> where D: IntoData<'d>, 'ctx: 'd, { let mut src = src.into_data()?; unsafe { return_err!(ffi::gpgme_op_keylist_from_data_start( ctx.as_raw(), src.borrow_mut().as_raw(), 0, )); } Ok(Keys { _src: src, ctx }) } } impl Iterator for Keys<'_, D> { type Item = Result; #[inline] fn next(&mut self) -> Option { unsafe { let mut key = ptr::null_mut(); match Error::new(ffi::gpgme_op_keylist_next(self.ctx.as_raw(), &mut key)) { Error::NO_ERROR => Some(Ok(Key::from_raw(key))), e if e.code() == Error::EOF.code() => None, e => Some(Err(e)), } } } } impl FusedIterator for Keys<'_, D> {} /// An iterator type yielding the `Key`s that would be used in a signing operation for a `Context`. #[derive(Clone)] pub struct Signers<'ctx> { ctx: &'ctx Context, current: Option, } impl Iterator for Signers<'_> { type Item = Key; #[inline] fn next(&mut self) -> Option { unsafe { self.current.and_then(|x| { match ffi::gpgme_signers_enum(self.ctx.as_raw(), x).as_mut() { Some(key) => { self.current = x.checked_add(1); Some(Key::from_raw(key)) } _ => { self.current = None; None } } }) } } #[inline] fn nth(&mut self, n: usize) -> Option { self.current = self .current .and_then(|x| n.value_into().ok().and_then(|n| x.checked_add(n))); self.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.current.map_or((0, Some(0)), |c| { let total = unsafe { ffi::gpgme_signers_count(self.ctx.as_raw()) }; match usize::try_from(total - (c as libc::c_uint)) { Ok(v) => (v, Some(v)), Err(_) => (usize::MAX, None), } }) } #[inline] fn count(self) -> usize { self.size_hint().0 } } impl FusedIterator for Signers<'_> {} impl fmt::Debug for Signers<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } /// A bundle containing a context for cryptographic operations and storage /// for various callbacks used by the context. /// /// Upstream documentation: /// [`gpgme_ctx_t`](https://www.gnupg.org/documentation/manuals/gpgme/Contexts.html#Contexts) pub struct ContextWithCallbacks<'a> { inner: Context, passphrase_hook: Option>, progress_hook: Option>, status_hook: Option>, } impl Drop for ContextWithCallbacks<'_> { fn drop(&mut self) { (**self).clear_passphrase_provider(); (**self).clear_progress_reporter(); (**self).clear_status_handler(); } } impl<'a> ContextWithCallbacks<'a> { /// Upstream documentation: /// [`gpgme_set_passphrase_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Passphrase-Callback.html#index-gpgme_005fset_005fpassphrase_005fcb) pub fn clear_passphrase_provider(&mut self) { (**self).clear_passphrase_provider(); self.passphrase_hook.take(); } /// Upstream documentation: /// [`gpgme_set_passphrase_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Passphrase-Callback.html#index-gpgme_005fset_005fpassphrase_005fcb) pub fn set_passphrase_provider

(&mut self, provider: P) where P: PassphraseProvider + 'a, { let mut hook = Box::new(callbacks::Hook::from(provider)); unsafe { ffi::gpgme_set_passphrase_cb( self.as_raw(), Some(callbacks::passphrase_cb::

), ptr::addr_of_mut!(*hook).cast(), ); } self.passphrase_hook = Some(hook); } /// Upstream documentation: /// [`gpgme_set_progress_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Progress-Meter-Callback.html#index-gpgme_005fset_005fprogress_005fcb) pub fn clear_progress_reporter(&mut self) { (**self).clear_progress_reporter(); self.progress_hook.take(); } /// Upstream documentation: /// [`gpgme_set_progress_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Progress-Meter-Callback.html#index-gpgme_005fset_005fprogress_005fcb) pub fn set_progress_reporter(&mut self, handler: H) where H: ProgressReporter + 'a, { let mut hook = Box::new(callbacks::Hook::from(handler)); unsafe { ffi::gpgme_set_progress_cb( self.as_raw(), Some(callbacks::progress_cb::), ptr::addr_of_mut!(*hook).cast(), ); } self.progress_hook = Some(hook); } /// Upstream documentation: /// [`gpgme_set_status_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Status-Message-Callback.html#index-gpgme_005fset_005fstatus_005fcb) pub fn clear_status_handler(&mut self) { (**self).clear_status_handler(); self.status_hook.take(); } /// Upstream documentation: /// [`gpgme_set_status_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Status-Message-Callback.html#index-gpgme_005fset_005fstatus_005fcb) pub fn set_status_handler(&mut self, handler: H) where H: StatusHandler + 'a, { let mut hook = Box::new(callbacks::Hook::from(handler)); unsafe { ffi::gpgme_set_status_cb( self.as_raw(), Some(callbacks::status_cb::), ptr::addr_of_mut!(*hook).cast(), ); } self.status_hook = Some(hook); } /// Returns the inner `Context` object. /// /// All currently set callbacks are cleared by this method. pub fn into_inner(mut self) -> Context { self.clear_passphrase_provider(); self.clear_progress_reporter(); self.clear_status_handler(); let this = ManuallyDrop::new(self); unsafe { Context::from_raw(this.as_raw()) } } } impl From for ContextWithCallbacks<'_> { fn from(inner: Context) -> Self { Self { passphrase_hook: None, progress_hook: None, status_hook: None, inner, } } } impl Deref for ContextWithCallbacks<'_> { type Target = Context; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for ContextWithCallbacks<'_> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl fmt::Debug for ContextWithCallbacks<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, f) } } fn with_joined_cstr(strings: I, f: F) -> R where I: IntoIterator, I::Item: CStrArgument, F: FnOnce(Option<&CStr>, usize) -> R, { let mut strings = strings.into_iter().fuse(); match (strings.next(), strings.next()) { (Some(first), Some(second)) => { let mut count = 2; let mut joined = Vec::new(); joined.extend_from_slice(first.into_cstr().as_ref().to_bytes()); joined.push(b'\n'); joined.extend_from_slice(second.into_cstr().as_ref().to_bytes()); for x in strings { joined.push(b'\n'); joined.extend_from_slice(x.into_cstr().as_ref().to_bytes()); count += 1; } f(Some(joined.into_cstr().as_ref()), count) } (Some(single), None) => f(Some(single.into_cstr().as_ref()), 1), _ => f(None, 0), } } gpgme-0.11.0/src/data.rs000064400000000000000000000473211046102023000130570ustar 00000000000000#[cfg(unix)] use std::os::unix::io::AsRawFd; use std::{ borrow::BorrowMut, error::Error as StdError, ffi::CStr, fmt, fs::File, io::{self, prelude::*, Cursor}, marker::PhantomData, ptr, slice, str::Utf8Error, }; use conv::{UnwrapOrSaturate, ValueInto}; use ffi; use libc; use static_assertions::{assert_impl_all, assert_not_impl_any}; use crate::{error::return_err, utils::CStrArgument, Error, NonNull, Result}; assert_impl_all!(Data<'_>: Send); assert_not_impl_any!(Data<'_>: Sync); ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_data_encoding_t`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-enum-gpgme_005fdata_005fencoding_005ft) #[non_exhaustive] pub enum Encoding: ffi::gpgme_data_encoding_t { None = ffi::GPGME_DATA_ENCODING_NONE, Binary = ffi::GPGME_DATA_ENCODING_BINARY, Base64 = ffi::GPGME_DATA_ENCODING_BASE64, Armor = ffi::GPGME_DATA_ENCODING_ARMOR, Url = ffi::GPGME_DATA_ENCODING_URL, UrlEscaped = ffi::GPGME_DATA_ENCODING_URLESC, Url0 = ffi::GPGME_DATA_ENCODING_URL0, Mime = ffi::GPGME_DATA_ENCODING_MIME, } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_data_type_t`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Convenience.html#index-enum-gpgme_005fdata_005ftype_005ft) #[non_exhaustive] pub enum Type: ffi::gpgme_data_type_t { Unknown = ffi::GPGME_DATA_TYPE_UNKNOWN, Invalid = ffi::GPGME_DATA_TYPE_INVALID, PgpSigned = ffi::GPGME_DATA_TYPE_PGP_SIGNED, PgpEncrypted = ffi::GPGME_DATA_TYPE_PGP_ENCRYPTED, PgpOther = ffi::GPGME_DATA_TYPE_PGP_OTHER, PgpKey = ffi::GPGME_DATA_TYPE_PGP_KEY, PgpSignature = ffi::GPGME_DATA_TYPE_PGP_SIGNATURE, CmsSigned = ffi::GPGME_DATA_TYPE_CMS_SIGNED, CmsEncrypted = ffi::GPGME_DATA_TYPE_CMS_ENCRYPTED, CmsOther = ffi::GPGME_DATA_TYPE_CMS_OTHER, X509Certificate = ffi::GPGME_DATA_TYPE_X509_CERT, Pkcs12 = ffi::GPGME_DATA_TYPE_PKCS12, } } #[derive(Clone)] pub struct WrappedError(Error, S); impl WrappedError { pub fn error(&self) -> Error { self.0 } pub fn into_inner(self) -> S { self.1 } } impl fmt::Debug for WrappedError { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.0, fmt) } } impl fmt::Display for WrappedError { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) } } impl StdError for WrappedError { fn description(&self) -> &str { #[allow(deprecated)] StdError::description(&self.0) } fn cause(&self) -> Option<&dyn StdError> { Some(&self.0) } fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.0) } } /// Upstream documentation: /// [`gpgme_data_t`](https://www.gnupg.org/documentation/manuals/gpgme/Exchanging-Data.html#Exchanging-Data) #[derive(Debug)] pub struct Data<'data>(NonNull, PhantomData<&'data mut ()>); unsafe impl Send for Data<'_> {} impl Drop for Data<'_> { #[inline] fn drop(&mut self) { unsafe { ffi::gpgme_data_release(self.as_raw()); } } } impl<'data> Data<'data> { impl_wrapper!(ffi::gpgme_data_t, PhantomData); #[inline] pub fn stdin() -> Result> { Data::from_reader(io::stdin()).map_err(|err| err.error()) } #[inline] pub fn stdout() -> Result> { Data::from_writer(io::stdout()).map_err(|err| err.error()) } #[inline] pub fn stderr() -> Result> { Data::from_writer(io::stderr()).map_err(|err| err.error()) } /// Constructs an empty data object. /// /// Upstream documentation: /// [`gpgme_data_new`](https://www.gnupg.org/documentation/manuals/gpgme/Memory-Based-Data-Buffers.html#index-gpgme_005fdata_005fnew) #[inline] pub fn new() -> Result> { crate::init(); unsafe { let mut data = ptr::null_mut(); return_err!(ffi::gpgme_data_new(&mut data)); Ok(Data::from_raw(data)) } } /// Constructs a data object and fills it with the contents of the file /// referenced by `path`. /// /// Upstream documentation: /// [`gpgme_data_new_from_file`](https://www.gnupg.org/documentation/manuals/gpgme/Memory-Based-Data-Buffers.html#index-gpgme_005fdata_005fnew_005ffrom_005ffile) #[inline] pub fn load(path: impl CStrArgument) -> Result> { crate::init(); let path = path.into_cstr(); unsafe { let mut data = ptr::null_mut(); return_err!(ffi::gpgme_data_new_from_file( &mut data, path.as_ref().as_ptr(), 1, )); Ok(Data::from_raw(data)) } } /// Constructs a data object and fills it with a copy of `bytes`. /// /// Upstream documentation: /// [`gpgme_data_new_from_mem`](https://www.gnupg.org/documentation/manuals/gpgme/Memory-Based-Data-Buffers.html#index-gpgme_005fdata_005fnew_005ffrom_005fmem) #[inline] pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result> { crate::init(); let bytes = bytes.as_ref(); unsafe { let (buf, len) = (bytes.as_ptr(), bytes.len()); let mut data = ptr::null_mut(); return_err!(ffi::gpgme_data_new_from_mem(&mut data, buf.cast(), len, 1)); Ok(Data::from_raw(data)) } } /// Constructs a data object which copies from `buf` as needed. /// /// Upstream documentation: /// [`gpgme_data_new_from_mem`](https://www.gnupg.org/documentation/manuals/gpgme/Memory-Based-Data-Buffers.html#index-gpgme_005fdata_005fnew_005ffrom_005fmem) #[inline] pub fn from_buffer(buf: &'data (impl AsRef<[u8]> + ?Sized)) -> Result { crate::init(); let buf = buf.as_ref(); unsafe { let (buf, len) = (buf.as_ptr(), buf.len()); let mut data = ptr::null_mut(); return_err!(ffi::gpgme_data_new_from_mem(&mut data, buf.cast(), len, 0)); Ok(Data::from_raw(data)) } } /// Upstream documentation: /// [`gpgme_data_new_from_fd`](https://www.gnupg.org/documentation/manuals/gpgme/File-Based-Data-Buffers.html#index-gpgme_005fdata_005fnew_005ffrom_005ffd) #[inline] #[cfg(unix)] pub fn from_fd(file: &'data (impl AsRawFd + ?Sized)) -> Result { crate::init(); unsafe { let mut data = ptr::null_mut(); return_err!(ffi::gpgme_data_new_from_fd(&mut data, file.as_raw_fd())); Ok(Data::from_raw(data)) } } /// Upstream documentation: /// [`gpgme_data_new_from_stream`](https://www.gnupg.org/documentation/manuals/gpgme/File-Based-Data-Buffers.html#index-gpgme_005fdata_005fnew_005ffrom_005fstream) #[inline] pub unsafe fn from_raw_file(file: *mut libc::FILE) -> Result { crate::init(); let mut data = ptr::null_mut(); return_err!(ffi::gpgme_data_new_from_stream(&mut data, file)); Ok(Data::from_raw(data)) } unsafe fn from_callbacks(cbs: ffi::gpgme_data_cbs, src: S) -> Result> where S: Send + 'data, { crate::init(); let src = Box::into_raw(Box::new(CallbackWrapper { inner: src, cbs })); let cbs = ptr::addr_of_mut!((*src).cbs); let mut data = ptr::null_mut(); let result = ffi::gpgme_data_new_from_cbs(&mut data, cbs, src.cast()); if result == 0 { Ok(Data::from_raw(data)) } else { Err(WrappedError(Error::new(result), Box::from_raw(src).inner)) } } #[inline] pub fn from_reader(r: R) -> Result> where R: Read + Send + 'data, { let cbs = ffi::gpgme_data_cbs { read: Some(read_callback::), write: None, seek: None, release: Some(release_callback::), }; unsafe { Data::from_callbacks(cbs, r) } } #[inline] pub fn from_seekable_reader(r: R) -> Result> where R: Read + Seek + Send + 'data, { let cbs = ffi::gpgme_data_cbs { read: Some(read_callback::), write: None, seek: Some(seek_callback::), release: Some(release_callback::), }; unsafe { Data::from_callbacks(cbs, r) } } #[inline] pub fn from_writer(w: W) -> Result> where W: Write + Send + 'data, { let cbs = ffi::gpgme_data_cbs { read: None, write: Some(write_callback::), seek: None, release: Some(release_callback::), }; unsafe { Data::from_callbacks(cbs, w) } } #[inline] pub fn from_seekable_writer(w: W) -> Result> where W: Write + Seek + Send + 'data, { let cbs = ffi::gpgme_data_cbs { read: None, write: Some(write_callback::), seek: Some(seek_callback::), release: Some(release_callback::), }; unsafe { Data::from_callbacks(cbs, w) } } #[inline] pub fn from_stream(s: S) -> Result> where S: Read + Write + Send + 'data, { let cbs = ffi::gpgme_data_cbs { read: Some(read_callback::), write: Some(write_callback::), seek: None, release: Some(release_callback::), }; unsafe { Data::from_callbacks(cbs, s) } } #[inline] pub fn from_seekable_stream(s: S) -> Result> where S: Read + Write + Seek + Send + 'data, { let cbs = ffi::gpgme_data_cbs { read: Some(read_callback::), write: Some(write_callback::), seek: Some(seek_callback::), release: Some(release_callback::), }; unsafe { Data::from_callbacks(cbs, s) } } /// Upstream documentation: /// [`gpgme_data_get_file_name`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-gpgme_005fdata_005fget_005ffile_005fname) #[inline] pub fn filename(&self) -> Result<&str, Option> { self.filename_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Upstream documentation: /// [`gpgme_data_get_file_name`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-gpgme_005fdata_005fget_005ffile_005fname) #[inline] pub fn filename_raw(&self) -> Option<&CStr> { unsafe { ffi::gpgme_data_get_file_name(self.as_raw()) .as_ref() .map(|s| CStr::from_ptr(s)) } } /// Upstream documentation: /// [`gpgme_data_set_file_name`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-gpgme_005fdata_005fset_005ffile_005fname) #[inline] pub fn clear_filename(&mut self) -> Result<()> { unsafe { return_err!(ffi::gpgme_data_set_file_name(self.as_raw(), ptr::null())); } Ok(()) } /// Upstream documentation: /// [`gpgme_data_set_file_name`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-gpgme_005fdata_005fset_005ffile_005fname) #[inline] pub fn set_filename(&mut self, name: impl CStrArgument) -> Result<()> { let name = name.into_cstr(); unsafe { return_err!(ffi::gpgme_data_set_file_name( self.as_raw(), name.as_ref().as_ptr(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_data_get_encoding`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-gpgme_005fdata_005fget_005fencoding) #[inline] pub fn encoding(&self) -> Encoding { unsafe { Encoding::from_raw(ffi::gpgme_data_get_encoding(self.as_raw())) } } /// Upstream documentation: /// [`gpgme_data_set_encoding`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-gpgme_005fdata_005fset_005fencoding) #[inline] pub fn set_encoding(&mut self, enc: Encoding) -> Result<()> { unsafe { return_err!(ffi::gpgme_data_set_encoding(self.as_raw(), enc.raw())) } Ok(()) } /// Upstream documentation: /// [`gpgme_data_set_flag`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Meta_002dData.html#index-gpgme_005fdata_005fset_005fflag) #[inline] pub fn set_flag(&mut self, name: impl CStrArgument, value: impl CStrArgument) -> Result<()> { let name = name.into_cstr(); let value = value.into_cstr(); unsafe { return_err!(ffi::gpgme_data_set_flag( self.as_raw(), name.as_ref().as_ptr(), value.as_ref().as_ptr(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_data_identify`](https://www.gnupg.org/documentation/manuals/gpgme/Data-Buffer-Convenience.html#index-gpgme_005fdata_005fidentify) #[inline] pub fn identify(&mut self) -> Type { unsafe { Type::from_raw(ffi::gpgme_data_identify(self.as_raw(), 0)) } } /// Upstream documentation: /// [`gpgme_data_release_and_get_mem`](https://www.gnupg.org/documentation/manuals/gpgme/Destroying-Data-Buffers.html#index-gpgme_005fdata_005frelease_005fand_005fget_005fmem) #[inline] pub fn try_into_bytes(self) -> Option> { unsafe { let mut len = 0; ffi::gpgme_data_release_and_get_mem(self.into_raw(), &mut len) .as_mut() .map(|b| { let r = slice::from_raw_parts(ptr::addr_of!(*b).cast(), len).to_vec(); ffi::gpgme_free(ptr::addr_of_mut!(*b).cast()); r }) } } } impl Read for Data<'_> { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { let result = unsafe { let (buf, len) = (buf.as_mut_ptr(), buf.len()); ffi::gpgme_data_read(self.as_raw(), buf.cast(), len) }; if result >= 0 { Ok(result as usize) } else { Err(Error::last_os_error().into()) } } } impl Write for Data<'_> { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { let result = unsafe { let (buf, len) = (buf.as_ptr(), buf.len()); ffi::gpgme_data_write(self.as_raw(), buf.cast(), len) }; Ok(usize::try_from(result).map_err(|_| Error::last_os_error())?) } #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl Seek for Data<'_> { #[inline] fn seek(&mut self, pos: io::SeekFrom) -> io::Result { let (off, whence) = match pos { io::SeekFrom::Start(off) => (off.value_into().unwrap_or_saturate(), libc::SEEK_SET), io::SeekFrom::End(off) => (off.value_into().unwrap_or_saturate(), libc::SEEK_END), io::SeekFrom::Current(off) => (off.value_into().unwrap_or_saturate(), libc::SEEK_CUR), }; let result = unsafe { ffi::gpgme_data_seek(self.as_raw(), off, whence) }; Ok(u64::try_from(result).map_err(|_| Error::last_os_error())?) } } struct CallbackWrapper { cbs: ffi::gpgme_data_cbs, inner: S, } unsafe extern "C" fn read_callback( handle: *mut libc::c_void, buffer: *mut libc::c_void, size: libc::size_t, ) -> libc::ssize_t { let handle = handle.cast::>(); let slice = slice::from_raw_parts_mut(buffer.cast::(), size); (*handle) .inner .read(slice) .map(|n| n as libc::ssize_t) .unwrap_or_else(|err| { ffi::gpgme_err_set_errno(Error::from(err).to_errno()); -1 }) } unsafe extern "C" fn write_callback( handle: *mut libc::c_void, buffer: *const libc::c_void, size: libc::size_t, ) -> libc::ssize_t { let handle = handle.cast::>(); let slice = slice::from_raw_parts(buffer.cast::(), size); (*handle) .inner .write(slice) .map(|n| n as libc::ssize_t) .unwrap_or_else(|err| { ffi::gpgme_err_set_errno(Error::from(err).to_errno()); -1 }) } unsafe extern "C" fn seek_callback( handle: *mut libc::c_void, offset: libc::off_t, whence: libc::c_int, ) -> libc::off_t { let handle = handle.cast::>(); let pos = match whence { libc::SEEK_SET => io::SeekFrom::Start(offset.value_into().unwrap_or_saturate()), libc::SEEK_END => io::SeekFrom::End(offset.value_into().unwrap_or_saturate()), libc::SEEK_CUR => io::SeekFrom::Current(offset.value_into().unwrap_or_saturate()), _ => { ffi::gpgme_err_set_errno(Error::EINVAL.to_errno()); return -1; } }; (*handle) .inner .seek(pos) .map(|n| n.value_into().unwrap_or_saturate()) .unwrap_or_else(|err| { ffi::gpgme_err_set_errno(Error::from(err).to_errno()); -1 }) } unsafe extern "C" fn release_callback(handle: *mut libc::c_void) { drop(Box::from_raw(handle.cast::>())); } /// A trait for converting compatible types into data objects. pub trait IntoData<'a> { type Output: BorrowMut>; fn into_data(self) -> Result; } impl<'a> IntoData<'a> for &mut Data<'a> { type Output = Self; fn into_data(self) -> Result { Ok(self) } } impl<'a> IntoData<'a> for Data<'a> { type Output = Self; fn into_data(self) -> Result { Ok(self) } } impl<'a> IntoData<'a> for &'a [u8] { type Output = Data<'a>; fn into_data(self) -> Result> { Data::from_seekable_reader(Cursor::new(self)).map_err(|e| e.error()) } } impl<'a> IntoData<'a> for &'a mut [u8] { type Output = Data<'a>; fn into_data(self) -> Result> { Data::from_seekable_stream(Cursor::new(self)).map_err(|e| e.error()) } } impl<'a> IntoData<'a> for &'a Vec { type Output = Data<'a>; fn into_data(self) -> Result> { self.as_slice().into_data() } } impl<'a> IntoData<'a> for &'a mut Vec { type Output = Data<'a>; fn into_data(self) -> Result> { Data::from_seekable_stream(Cursor::new(self)).map_err(|e| e.error()) } } impl IntoData<'static> for Vec { type Output = Data<'static>; fn into_data(self) -> Result> { Data::from_seekable_stream(Cursor::new(self)).map_err(|e| e.error()) } } impl<'a> IntoData<'a> for &'a str { type Output = Data<'a>; fn into_data(self) -> Result> { self.as_bytes().into_data() } } impl IntoData<'static> for String { type Output = Data<'static>; fn into_data(self) -> Result> { self.into_bytes().into_data() } } impl<'a> IntoData<'a> for &'a File { type Output = Data<'a>; fn into_data(self) -> Result> { Data::from_seekable_stream(self).map_err(|e| e.error()) } } impl<'a> IntoData<'a> for &'a mut File { type Output = Data<'a>; fn into_data(self) -> Result> { Data::from_seekable_stream(self).map_err(|e| e.error()) } } impl IntoData<'static> for File { type Output = Data<'static>; fn into_data(self) -> Result> { Data::from_seekable_stream(self).map_err(|e| e.error()) } } gpgme-0.11.0/src/edit.rs000064400000000000000000000202571046102023000130720ustar 00000000000000#![allow(non_camel_case_types, deprecated)] use std::{fmt, io::prelude::*, panic::UnwindSafe}; use ffi; use crate::{Error, Result}; pub use crate::{EditInteractionStatus, EditInteractor}; ffi_enum_wrapper! { #[non_exhaustive] pub enum StatusCode: ffi::gpgme_status_code_t { Eof = ffi::GPGME_STATUS_EOF, Enter = ffi::GPGME_STATUS_ENTER, Leave = ffi::GPGME_STATUS_LEAVE, Abort = ffi::GPGME_STATUS_ABORT, GoodSig = ffi::GPGME_STATUS_GOODSIG, BadSig = ffi::GPGME_STATUS_BADSIG, ErrSig = ffi::GPGME_STATUS_ERRSIG, BadArmor = ffi::GPGME_STATUS_BADARMOR, RsaOrIdea = ffi::GPGME_STATUS_RSA_OR_IDEA, KeyExpired = ffi::GPGME_STATUS_KEYEXPIRED, KeyRevoked = ffi::GPGME_STATUS_KEYREVOKED, TrustUndefined = ffi::GPGME_STATUS_TRUST_UNDEFINED, TrustNever = ffi::GPGME_STATUS_TRUST_NEVER, TrustMarginal = ffi::GPGME_STATUS_TRUST_MARGINAL, TrustFully = ffi::GPGME_STATUS_TRUST_FULLY, TrustUltimate = ffi::GPGME_STATUS_TRUST_ULTIMATE, ShmInfo = ffi::GPGME_STATUS_SHM_INFO, ShmGet = ffi::GPGME_STATUS_SHM_GET, ShmGetBool = ffi::GPGME_STATUS_SHM_GET_BOOL, ShmGetHidden = ffi::GPGME_STATUS_SHM_GET_HIDDEN, NeedPassphrase = ffi::GPGME_STATUS_NEED_PASSPHRASE, ValidSig = ffi::GPGME_STATUS_VALIDSIG, SigId = ffi::GPGME_STATUS_SIG_ID, EncTo = ffi::GPGME_STATUS_ENC_TO, NoData = ffi::GPGME_STATUS_NODATA, BadPassphrase = ffi::GPGME_STATUS_BAD_PASSPHRASE, NoPubKey = ffi::GPGME_STATUS_NO_PUBKEY, NoSecKey = ffi::GPGME_STATUS_NO_SECKEY, NeedPassphraseSym = ffi::GPGME_STATUS_NEED_PASSPHRASE_SYM, DecryptionFailed = ffi::GPGME_STATUS_DECRYPTION_FAILED, DecryptionOkay = ffi::GPGME_STATUS_DECRYPTION_OKAY, MissingPassphrase = ffi::GPGME_STATUS_MISSING_PASSPHRASE, GoodPassphrase = ffi::GPGME_STATUS_GOOD_PASSPHRASE, GoodMdc = ffi::GPGME_STATUS_GOODMDC, BadMdc = ffi::GPGME_STATUS_BADMDC, ErrMdc = ffi::GPGME_STATUS_ERRMDC, Imported = ffi::GPGME_STATUS_IMPORTED, ImportOk = ffi::GPGME_STATUS_IMPORT_OK, ImportProblem = ffi::GPGME_STATUS_IMPORT_PROBLEM, ImportRes = ffi::GPGME_STATUS_IMPORT_RES, FileStart = ffi::GPGME_STATUS_FILE_START, FileDone = ffi::GPGME_STATUS_FILE_DONE, FileError = ffi::GPGME_STATUS_FILE_ERROR, BeginDecryption = ffi::GPGME_STATUS_BEGIN_DECRYPTION, EndDecryption = ffi::GPGME_STATUS_END_DECRYPTION, BeginEncryption = ffi::GPGME_STATUS_BEGIN_ENCRYPTION, EndEncryption = ffi::GPGME_STATUS_END_ENCRYPTION, DeleteProblem = ffi::GPGME_STATUS_DELETE_PROBLEM, GetBool = ffi::GPGME_STATUS_GET_BOOL, GetLine = ffi::GPGME_STATUS_GET_LINE, GetHidden = ffi::GPGME_STATUS_GET_HIDDEN, GotIt = ffi::GPGME_STATUS_GOT_IT, Progress = ffi::GPGME_STATUS_PROGRESS, SigCreated = ffi::GPGME_STATUS_SIG_CREATED, SessionKey = ffi::GPGME_STATUS_SESSION_KEY, NotationName = ffi::GPGME_STATUS_NOTATION_NAME, NotationData = ffi::GPGME_STATUS_NOTATION_DATA, PolicyUrl = ffi::GPGME_STATUS_POLICY_URL, BeginStream = ffi::GPGME_STATUS_BEGIN_STREAM, EndStream = ffi::GPGME_STATUS_END_STREAM, KeyCreated = ffi::GPGME_STATUS_KEY_CREATED, UserIdHint = ffi::GPGME_STATUS_USERID_HINT, Unexpected = ffi::GPGME_STATUS_UNEXPECTED, InvRecp = ffi::GPGME_STATUS_INV_RECP, NoRecp = ffi::GPGME_STATUS_NO_RECP, AlreadySigned = ffi::GPGME_STATUS_ALREADY_SIGNED, SigExpired = ffi::GPGME_STATUS_SIGEXPIRED, ExpSig = ffi::GPGME_STATUS_EXPSIG, ExpKeySig = ffi::GPGME_STATUS_EXPKEYSIG, Truncated = ffi::GPGME_STATUS_TRUNCATED, Error = ffi::GPGME_STATUS_ERROR, NewSig = ffi::GPGME_STATUS_NEWSIG, RevKeySig = ffi::GPGME_STATUS_REVKEYSIG, SigSubpacket = ffi::GPGME_STATUS_SIG_SUBPACKET, NeedPassphrasePin = ffi::GPGME_STATUS_NEED_PASSPHRASE_PIN, ScOpFailure = ffi::GPGME_STATUS_SC_OP_FAILURE, ScOpSuccess = ffi::GPGME_STATUS_SC_OP_SUCCESS, CardCtrl = ffi::GPGME_STATUS_CARDCTRL, BackupKeyCreated = ffi::GPGME_STATUS_BACKUP_KEY_CREATED, PkaTrustBad = ffi::GPGME_STATUS_PKA_TRUST_BAD, PkaTrustGood = ffi::GPGME_STATUS_PKA_TRUST_GOOD, Plaintext = ffi::GPGME_STATUS_PLAINTEXT, InvSgnr = ffi::GPGME_STATUS_INV_SGNR, NoSgnr = ffi::GPGME_STATUS_NO_SGNR, Success = ffi::GPGME_STATUS_SUCCESS, DecryptionInfo = ffi::GPGME_STATUS_DECRYPTION_INFO, PlaintextLength = ffi::GPGME_STATUS_PLAINTEXT_LENGTH, Mountpoint = ffi::GPGME_STATUS_MOUNTPOINT, PinentryLaunched = ffi::GPGME_STATUS_PINENTRY_LAUNCHED, Attribute = ffi::GPGME_STATUS_ATTRIBUTE, BeginSigning = ffi::GPGME_STATUS_BEGIN_SIGNING, KeyNotCreated = ffi::GPGME_STATUS_KEY_NOT_CREATED, InquireMaxLen = ffi::GPGME_STATUS_INQUIRE_MAXLEN, Failure = ffi::GPGME_STATUS_FAILURE, KeyConsidered = ffi::GPGME_STATUS_KEY_CONSIDERED, TofuUser = ffi::GPGME_STATUS_TOFU_USER, TofuStats = ffi::GPGME_STATUS_TOFU_STATS, TofuStatsLong = ffi::GPGME_STATUS_TOFU_STATS_LONG, NotationFlags = ffi::GPGME_STATUS_NOTATION_FLAGS, } } impl StatusCode { pub fn needs_response(&self) -> bool { match self { Self::AlreadySigned | Self::Error | Self::GetBool | Self::GetLine | Self::KeyCreated | Self::NeedPassphraseSym | Self::ScOpFailure | Self::CardCtrl | Self::BackupKeyCreated => true, _ => false, } } pub fn into_result(self) -> Result<()> { match self { Self::MissingPassphrase => Err(Error::NO_PASSPHRASE), Self::AlreadySigned => Err(Error::USER_1), Self::SigExpired => Err(Error::SIG_EXPIRED), _ => Ok(()), } } } // Actions pub const QUIT: &str = "quit"; pub const SAVE: &str = "save"; pub const YES: &str = "Y"; pub const NO: &str = "N"; // Keywords pub const PROMPT: &str = "keyedit.prompt"; pub const CONFIRM_SAVE: &str = "keyedit.save.okay"; pub const CONFIRM_CANCEL: &str = "keyedit.cancel.okay"; pub const CONFIRM_KEY_VALID: &str = "keygen.valid.okay"; pub const CONFIRM_CREATE_KEY: &str = "keygen.sub.okay"; pub const KEY_NAME: &str = "keygen.name"; pub const KEY_EMAIL: &str = "keygen.email"; pub const KEY_COMMENT: &str = "keygen.comment"; pub const KEY_VALID: &str = "keygen.valid"; pub const KEY_FLAGS: &str = "keygen.flags"; pub const KEY_SIZE: &str = "keygen.size"; pub const KEY_ALGORITHM: &str = "keygen.algo"; pub const KEY_UID_COMMAND: &str = "keygen.userid.cmd"; pub const KEY_CURVE: &str = "keygen.curve"; pub trait Editor: UnwindSafe + Send { type State: fmt::Debug + Default + Eq + Copy + UnwindSafe + Send; fn next_state( state: Result, status: EditInteractionStatus<'_>, need_response: bool, ) -> Result; fn action(&self, state: Self::State, out: &mut dyn Write) -> Result<()>; } #[derive(Debug)] pub struct EditorWrapper { editor: E, state: Result, } impl EditorWrapper { pub fn new(editor: E) -> EditorWrapper { EditorWrapper { editor, state: Ok(E::State::default()), } } } impl EditInteractor for EditorWrapper { fn interact( &mut self, status: EditInteractionStatus<'_>, out: Option<&mut dyn Write>, ) -> Result<()> { let old_state = self.state; self.state = status .code .into_result() .and_then(|_| E::next_state(self.state, status, out.is_some())) .and_then(|state| { if old_state == Ok(state) { return Ok(state); } out.map_or(Ok(()), |out| { self.editor .action(state, out) .and_then(|_| out.write_all(b"\n").map_err(Error::from)) }) .and(Ok(state)) }); self.state.and(Ok(())) } } gpgme-0.11.0/src/engine.rs000064400000000000000000000107061046102023000134100ustar 00000000000000use std::{ ffi::CStr, fmt, marker::PhantomData, ptr, str::Utf8Error, sync::{RwLock, RwLockReadGuard}, }; use ffi; use crate::{error::return_err, NonNull, Protocol, Result}; /// Upstream documentation: /// [`gpgme_engine_info_t`](https://www.gnupg.org/documentation/manuals/gpgme/Engine-Information.html#index-gpgme_005fengine_005finfo_005ft) #[derive(Copy, Clone)] pub struct EngineInfo<'a>(NonNull, PhantomData<&'a ()>); unsafe impl Send for EngineInfo<'_> {} unsafe impl Sync for EngineInfo<'_> {} impl EngineInfo<'_> { impl_wrapper!(ffi::gpgme_engine_info_t, PhantomData); /// Returns the `Protocol` implemented by the engine. #[inline] pub fn protocol(&self) -> Protocol { unsafe { Protocol::from_raw((*self.as_raw()).protocol) } } #[inline] pub fn path(&self) -> Result<&str, Option> { self.path_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn path_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .file_name .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn home_dir(&self) -> Result<&str, Option> { self.home_dir_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn home_dir_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .home_dir .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn check_version(&self, v: &str) -> bool { self.version() .map(|s| { let it1 = s.split('.').scan((), |_, x| x.parse::().ok()); let it2 = v.split('.').scan((), |_, x| x.parse::().ok()); Iterator::ge(it1, it2) }) .unwrap_or(false) } #[inline] pub fn version(&self) -> Result<&str, Option> { self.version_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn version_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()).version.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn required_version(&self) -> Result<&str, Option> { self.required_version_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn required_version_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .req_version .as_ref() .map(|s| CStr::from_ptr(s)) } } } impl fmt::Debug for EngineInfo<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EngineInfo") .field("raw", &self.as_raw()) .field("protocol", &self.protocol()) .field("path", &self.path_raw()) .field("home_dir", &self.home_dir_raw()) .field("version", &self.version_raw()) .field("required_version", &self.required_version_raw()) .finish() } } impl_list_iterator!(pub struct EngineInfos(EngineInfo: ffi::gpgme_engine_info_t)); /// A RAII guard type that ensures the global engine information list is not modified /// while it is being iterated. pub struct EngineInfoGuard(RwLockReadGuard<'static, ()>); impl EngineInfoGuard { pub fn new(lock: &'static RwLock<()>) -> Result { let lock = lock.read().expect("engine info lock was poisoned"); unsafe { let mut info = ptr::null_mut(); return_err!(ffi::gpgme_get_engine_info(&mut info)); } Ok(EngineInfoGuard(lock)) } #[inline] pub fn get(&self, proto: Protocol) -> Option> { self.into_iter().find(|info| info.protocol() == proto) } #[inline] pub fn iter(&self) -> EngineInfos<'_> { self.into_iter() } } impl<'a> IntoIterator for &'a EngineInfoGuard { type Item = EngineInfo<'a>; type IntoIter = EngineInfos<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { unsafe { let mut first = ptr::null_mut(); assert_eq!(ffi::gpgme_get_engine_info(&mut first), 0); EngineInfos::from_list(first) } } } impl fmt::Debug for EngineInfoGuard { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("EngineInfoGuard(..)") } } gpgme-0.11.0/src/flags.rs000064400000000000000000000264231046102023000132420ustar 00000000000000#![allow(trivial_numeric_casts)] use std::{ffi::CStr, fmt, str::Utf8Error}; use bitflags::bitflags; use ffi; use libc; bitflags! { /// Upstream documentation: /// [`gpgme_keylist_mode_t`](https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html#Key-Listing-Mode) pub struct KeyListMode: ffi::gpgme_keylist_mode_t { const LOCAL = ffi::GPGME_KEYLIST_MODE_LOCAL; const EXTERN = ffi::GPGME_KEYLIST_MODE_EXTERN; const SIGS = ffi::GPGME_KEYLIST_MODE_SIGS; const SIG_NOTATIONS = ffi::GPGME_KEYLIST_MODE_SIG_NOTATIONS; const WITH_SECRET = ffi::GPGME_KEYLIST_MODE_WITH_SECRET; const WITH_KEYGRIP = ffi::GPGME_KEYLIST_MODE_WITH_KEYGRIP; const WITH_TOFU = ffi::GPGME_KEYLIST_MODE_WITH_TOFU; const EPHEMERAL = ffi::GPGME_KEYLIST_MODE_EPHEMERAL; const VALIDATE = ffi::GPGME_KEYLIST_MODE_VALIDATE; const FORCE_EXTERN = ffi::GPGME_KEYLIST_MODE_FORCE_EXTERN; const LOCATE = ffi::GPGME_KEYLIST_MODE_LOCATE; const LOCATE_EXTERNAL = ffi::GPGME_KEYLIST_MODE_LOCATE_EXTERNAL; } } bitflags! { pub struct InteractFlags: libc::c_uint { const CARD = ffi::GPGME_INTERACT_CARD; } } bitflags! { /// Upstream documentation: /// [`gpgme_op_createkey`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fop_005fcreatekey) pub struct CreateKeyFlags: libc::c_uint { const SIGN = ffi::GPGME_CREATE_SIGN; const ENCR = ffi::GPGME_CREATE_ENCR; const CERT = ffi::GPGME_CREATE_CERT; const AUTH = ffi::GPGME_CREATE_AUTH; const NOPASSWD = ffi::GPGME_CREATE_NOPASSWD; const SELFSIGNED = ffi::GPGME_CREATE_SELFSIGNED; const NOSTORE = ffi::GPGME_CREATE_NOSTORE; const WANTPUB = ffi::GPGME_CREATE_WANTPUB; const WANTSEC = ffi::GPGME_CREATE_WANTSEC; const FORCE = ffi::GPGME_CREATE_FORCE; const NOEXPIRE = ffi::GPGME_CREATE_NOEXPIRE; } } bitflags! { /// Upstream documentation: /// [`gpgme_op_delete_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Deleting-Keys.html#index-gpgme_005fop_005fdelete_005fext) pub struct DeleteKeyFlags: libc::c_uint { const ALLOW_SECRET = ffi::GPGME_DELETE_ALLOW_SECRET; const FORCE = ffi::GPGME_DELETE_FORCE; } } bitflags! { /// Upstream documentation: /// [`gpgme_op_keysign`](https://www.gnupg.org/documentation/manuals/gpgme/Signing-Keys.html#index-gpgme_005fop_005fkeysign) pub struct KeySigningFlags: libc::c_uint { const LOCAL = ffi::GPGME_KEYSIGN_LOCAL; const LFSEP = ffi::GPGME_KEYSIGN_LFSEP; const NOEXPIRE = ffi::GPGME_KEYSIGN_NOEXPIRE; const FORCE = ffi::GPGME_KEYSIGN_FORCE; } } bitflags! { /// Upstream documentation: /// [`gpgme_import_status_t`](https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html#index-gpgme_005fimport_005fstatus_005ft) pub struct ImportFlags: libc::c_uint { const NEW = ffi::GPGME_IMPORT_NEW; const UID = ffi::GPGME_IMPORT_UID; const SIG = ffi::GPGME_IMPORT_SIG; const SUBKEY = ffi::GPGME_IMPORT_SUBKEY; const SECRET = ffi::GPGME_IMPORT_SECRET; } } bitflags! { /// Upstream documentation: /// [`gpgme_export_mode_t`](https://www.gnupg.org/documentation/manuals/gpgme/Exporting-Keys.html) pub struct ExportMode: ffi::gpgme_export_mode_t { const EXTERN = ffi::GPGME_EXPORT_MODE_EXTERN; const MINIMAL = ffi::GPGME_EXPORT_MODE_MINIMAL; const SECRET = ffi::GPGME_EXPORT_MODE_SECRET; const RAW = ffi::GPGME_EXPORT_MODE_RAW; const PKCS12 = ffi::GPGME_EXPORT_MODE_PKCS12; const SSH = ffi::GPGME_EXPORT_MODE_SSH; const SECRET_SUBKEY = ffi::GPGME_EXPORT_MODE_SECRET_SUBKEY; } } bitflags! { /// Upstream documentation: /// [`gpgme_op_encrypt`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fop_005fencrypt) pub struct EncryptFlags: ffi::gpgme_encrypt_flags_t { const ALWAYS_TRUST = ffi::GPGME_ENCRYPT_ALWAYS_TRUST; const NO_ENCRYPT_TO = ffi::GPGME_ENCRYPT_NO_ENCRYPT_TO; const PREPARE = ffi::GPGME_ENCRYPT_PREPARE; const EXPECT_SIGN = ffi::GPGME_ENCRYPT_EXPECT_SIGN; const NO_COMPRESS= ffi::GPGME_ENCRYPT_NO_COMPRESS; const SYMMETRIC = ffi::GPGME_ENCRYPT_SYMMETRIC; const THROW_KEYIDS = ffi::GPGME_ENCRYPT_THROW_KEYIDS; const WRAP = ffi::GPGME_ENCRYPT_WRAP; const WANT_ADDRESS = ffi::GPGME_ENCRYPT_WANT_ADDRESS; } } bitflags! { /// Upstream documentation: /// [`gpgme_op_decrypt_ext`](https://www.gnupg.org/documentation/manuals/gpgme/Decrypt.html#index-gpgme_005fop_005fdecrypt_005fext) pub struct DecryptFlags: ffi::gpgme_decrypt_flags_t { const VERIFY = ffi::GPGME_DECRYPT_VERIFY; const UNWRAP = ffi::GPGME_DECRYPT_UNWRAP; } } bitflags! { /// Upstream documentation: /// [`gpgme_sigsum_t`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fsignature_005ft) pub struct SignatureSummary: ffi::gpgme_sigsum_t { const VALID = ffi::GPGME_SIGSUM_VALID; const GREEN = ffi::GPGME_SIGSUM_GREEN; const RED = ffi::GPGME_SIGSUM_RED; const KEY_REVOKED = ffi::GPGME_SIGSUM_KEY_REVOKED; const KEY_EXPIRED = ffi::GPGME_SIGSUM_KEY_EXPIRED; const SIG_EXPIRED = ffi::GPGME_SIGSUM_SIG_EXPIRED; const KEY_MISSING = ffi::GPGME_SIGSUM_KEY_MISSING; const CRL_MISSING = ffi::GPGME_SIGSUM_CRL_MISSING; const CRL_TOO_OLD = ffi::GPGME_SIGSUM_CRL_TOO_OLD; const BAD_POLICY = ffi::GPGME_SIGSUM_BAD_POLICY; const SYS_ERROR = ffi::GPGME_SIGSUM_SYS_ERROR; const TOFU_CONFLICT = ffi::GPGME_SIGSUM_TOFU_CONFLICT; } } bitflags! { /// Upstream documentation: /// [`gpgme_sig_notation_flags_t`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fsig_005fnotation_005ft) pub struct SignatureNotationFlags: ffi::gpgme_sig_notation_flags_t { const HUMAN_READABLE = ffi::GPGME_SIG_NOTATION_HUMAN_READABLE; const CRITICAL = ffi::GPGME_SIG_NOTATION_CRITICAL; } } bitflags! { /// Upstream documentation: /// [`gpgme_op_getauditlog`](https://www.gnupg.org/documentation/manuals/gpgme/Additional-Logs.html#index-gpgme_005fop_005fgetauditlog) pub struct AuditLogFlags: libc::c_uint { const DEFAULT = ffi::GPGME_AUDITLOG_DEFAULT; const HTML = ffi::GPGME_AUDITLOG_HTML; const DIAG = ffi::GPGME_AUDITLOG_DIAG; const WITH_HELP = ffi::GPGME_AUDITLOG_WITH_HELP; } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_keyorg_t`](https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#index-gpgme_005fuser_005fid_005ft) #[non_exhaustive] pub enum KeyOrigin: ffi::gpgme_keyorg_t { Unknown = ffi::GPGME_KEYORG_UNKNOWN, KeyServer = ffi::GPGME_KEYORG_KS, Dane = ffi::GPGME_KEYORG_DANE, Wkd = ffi::GPGME_KEYORG_WKD, Url = ffi::GPGME_KEYORG_URL, File = ffi::GPGME_KEYORG_FILE, Self_ = ffi::GPGME_KEYORG_SELF, } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_sig_mode_t`](https://www.gnupg.org/documentation/manuals/gpgme/Creating-a-Signature.html#index-enum-gpgme_005fsig_005fmode_005ft) pub enum SignMode: ffi::gpgme_sig_mode_t { Normal = ffi::GPGME_SIG_MODE_NORMAL, Detached = ffi::GPGME_SIG_MODE_DETACH, Clear = ffi::GPGME_SIG_MODE_CLEAR, } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_pubkey_algo_t`](https://www.gnupg.org/documentation/manuals/gpgme/Public-Key-Algorithms.html#index-gpgme_005fpubkey_005falgo_005ft) #[non_exhaustive] pub enum KeyAlgorithm: ffi::gpgme_pubkey_algo_t { Rsa = ffi::GPGME_PK_RSA, RsaEncrypt = ffi::GPGME_PK_RSA_E, RsaSign = ffi::GPGME_PK_RSA_S, ElgamalEncrypt = ffi::GPGME_PK_ELG_E, Dsa = ffi::GPGME_PK_DSA, Ecc = ffi::GPGME_PK_ECC, Elgamal = ffi::GPGME_PK_ELG, Ecdsa = ffi::GPGME_PK_ECDSA, Ecdh = ffi::GPGME_PK_ECDH, Eddsa = ffi::GPGME_PK_EDDSA, } } impl KeyAlgorithm { /// Upstream documentation: /// [`gpgme_pubkey_algo_name`](https://www.gnupg.org/documentation/manuals/gpgme/Public-Key-Algorithms.html#index-gpgme_005fpubkey_005falgo_005fname) #[inline] pub fn name(&self) -> Result<&'static str, Option> { self.name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Upstream documentation: /// [`gpgme_pubkey_algo_name`](https://www.gnupg.org/documentation/manuals/gpgme/Public-Key-Algorithms.html#index-gpgme_005fpubkey_005falgo_005fname) #[inline] pub fn name_raw(&self) -> Option<&'static CStr> { unsafe { ffi::gpgme_pubkey_algo_name(self.raw()) .as_ref() .map(|s| CStr::from_ptr(s)) } } } impl fmt::Display for KeyAlgorithm { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.name().unwrap_or("Unknown")) } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_hash_algo_t`](https://www.gnupg.org/documentation/manuals/gpgme/Hash-Algorithms.html#index-enum-gpgme_005fhash_005falgo_005ft) #[non_exhaustive] pub enum HashAlgorithm: ffi::gpgme_hash_algo_t { None = ffi::GPGME_MD_NONE, Md2 = ffi::GPGME_MD_MD2, Md4 = ffi::GPGME_MD_MD4, Md5 = ffi::GPGME_MD_MD5, Sha1 = ffi::GPGME_MD_SHA1, Sha224 = ffi::GPGME_MD_SHA224, Sha256 = ffi::GPGME_MD_SHA256, Sha384 = ffi::GPGME_MD_SHA384, Sha512 = ffi::GPGME_MD_SHA512, RipeMd160 = ffi::GPGME_MD_RMD160, Tiger = ffi::GPGME_MD_TIGER, Haval = ffi::GPGME_MD_HAVAL, Crc32 = ffi::GPGME_MD_CRC32, Crc32Rfc1510 = ffi::GPGME_MD_CRC32_RFC1510, CrC24Rfc2440 = ffi::GPGME_MD_CRC24_RFC2440, } } impl HashAlgorithm { /// Upstream documentation: /// [`gpgme_hash_algo_name`](https://www.gnupg.org/documentation/manuals/gpgme/Hash-Algorithms.html#index-gpgme_005fhash_005falgo_005fname) #[inline] pub fn name(&self) -> Result<&'static str, Option> { self.name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Upstream documentation: /// [`gpgme_hash_algo_name`](https://www.gnupg.org/documentation/manuals/gpgme/Hash-Algorithms.html#index-gpgme_005fhash_005falgo_005fname) #[inline] pub fn name_raw(&self) -> Option<&'static CStr> { unsafe { ffi::gpgme_hash_algo_name(self.raw()) .as_ref() .map(|s| CStr::from_ptr(s)) } } } impl fmt::Display for HashAlgorithm { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.name().unwrap_or("Unknown")) } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_pinentry_mode_t`](https://www.gnupg.org/documentation/manuals/gpgme/Pinentry-Mode.html#index-gpgme_005fpinentry_005fmode_005ft) pub enum PinentryMode: ffi::gpgme_pinentry_mode_t { Default = ffi::GPGME_PINENTRY_MODE_DEFAULT, Ask = ffi::GPGME_PINENTRY_MODE_ASK, Cancel = ffi::GPGME_PINENTRY_MODE_CANCEL, Error = ffi::GPGME_PINENTRY_MODE_ERROR, Loopback = ffi::GPGME_PINENTRY_MODE_LOOPBACK, } } gpgme-0.11.0/src/keys.rs000064400000000000000000000630301046102023000131140ustar 00000000000000use std::{ ffi::CStr, fmt, marker::PhantomData, str::Utf8Error, time::{Duration, SystemTime, UNIX_EPOCH}, }; use cstr_argument::CStrArgument; use ffi; use crate::{ notation::SignatureNotations, Context, Error, KeyAlgorithm, KeyListMode, NonNull, Protocol, Result, Validity, }; /// Upstream documentation: /// [`gpgme_key_t`](https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#index-gpgme_005fkey_005ft) pub struct Key(NonNull); unsafe impl Send for Key {} unsafe impl Sync for Key {} impl Drop for Key { #[inline] fn drop(&mut self) { unsafe { ffi::gpgme_key_unref(self.as_raw()); } } } impl Clone for Key { #[inline] fn clone(&self) -> Key { unsafe { ffi::gpgme_key_ref(self.as_raw()); Key(self.0) } } } impl Key { impl_wrapper!(ffi::gpgme_key_t); #[inline] pub fn is_bad(&self) -> bool { self.is_revoked() || self.is_expired() || self.is_disabled() || self.is_invalid() } #[inline] pub fn is_revoked(&self) -> bool { unsafe { (*self.as_raw()).revoked() } } #[inline] pub fn is_expired(&self) -> bool { unsafe { (*self.as_raw()).expired() } } #[inline] pub fn is_disabled(&self) -> bool { unsafe { (*self.as_raw()).disabled() } } #[inline] pub fn is_invalid(&self) -> bool { unsafe { (*self.as_raw()).invalid() } } #[inline] pub fn can_encrypt(&self) -> bool { unsafe { (*self.as_raw()).can_encrypt() } } #[inline] pub fn can_sign(&self) -> bool { unsafe { (*self.as_raw()).can_sign() } } #[inline] pub fn can_certify(&self) -> bool { unsafe { (*self.as_raw()).can_certify() } } #[inline] pub fn can_authenticate(&self) -> bool { unsafe { (*self.as_raw()).can_authenticate() } } #[inline] pub fn is_qualified(&self) -> bool { unsafe { (*self.as_raw()).is_qualified() } } #[inline] pub fn is_de_vs(&self) -> bool { self.subkeys().all(|x| x.is_de_vs()) } #[inline] pub fn has_secret(&self) -> bool { unsafe { (*self.as_raw()).secret() } } #[inline] pub fn is_root(&self) -> bool { if let (Some(fpr), Some(chain_id)) = (self.fingerprint_raw(), self.chain_id_raw()) { fpr.to_bytes().eq_ignore_ascii_case(chain_id.to_bytes()) } else { false } } #[inline] pub fn owner_trust(&self) -> Validity { unsafe { Validity::from_raw((*self.as_raw()).owner_trust) } } #[inline] pub fn protocol(&self) -> Protocol { unsafe { Protocol::from_raw((*self.as_raw()).protocol) } } #[inline] pub fn issuer_serial(&self) -> Result<&str, Option> { self.issuer_serial_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn issuer_serial_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .issuer_serial .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn issuer_name(&self) -> Result<&str, Option> { self.issuer_name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn issuer_name_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .issuer_name .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn chain_id(&self) -> Result<&str, Option> { self.chain_id_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn chain_id_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .chain_id .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn id(&self) -> Result<&str, Option> { self.primary_key().map_or(Err(None), |k| k.id()) } #[inline] pub fn id_raw(&self) -> Option<&CStr> { self.primary_key()?.id_raw() } #[inline] pub fn short_id(&self) -> Result<&str, Option> { self.short_id_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn short_id_raw(&self) -> Option<&CStr> { self.id_raw().map(|s| { let bytes = s.to_bytes_with_nul(); if bytes.len() >= 9 { // One extra for the null terminator unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[(bytes.len() - 9)..]) } } else { s } }) } #[inline] pub fn fingerprint(&self) -> Result<&str, Option> { self.fingerprint_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn fingerprint_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .fpr .as_ref() .map(|s| CStr::from_ptr(s)) .or_else(|| self.primary_key()?.fingerprint_raw()) } } #[inline] pub fn key_list_mode(&self) -> KeyListMode { unsafe { KeyListMode::from_bits_truncate((*self.as_raw()).keylist_mode) } } #[inline] pub fn origin(&self) -> crate::KeyOrigin { unsafe { crate::KeyOrigin::from_raw((*self.as_raw()).origin()) } } #[inline] pub fn primary_key(&self) -> Option> { self.subkeys().next() } #[inline] pub fn user_ids(&self) -> UserIds<'_> { unsafe { UserIds::from_list((*self.as_raw()).uids) } } #[inline] pub fn subkeys(&self) -> Subkeys<'_> { unsafe { Subkeys::from_list((*self.as_raw()).subkeys) } } #[inline] pub fn last_update(&self) -> SystemTime { let timestamp = unsafe { (*self.as_raw()).last_update }; UNIX_EPOCH + Duration::from_secs(timestamp.into()) } #[inline] pub fn update(&mut self) -> Result<()> { *self = self.updated()?; Ok(()) } #[inline] pub fn updated(&self) -> Result { let mut ctx = Context::from_protocol(self.protocol())?; let _ = ctx.set_key_list_mode(self.key_list_mode()); ctx.refresh_key(self) } #[inline] pub fn add_uid(&self, userid: impl CStrArgument) -> Result<()> { let mut ctx = Context::from_protocol(self.protocol())?; ctx.add_uid(self, userid) } } impl fmt::Debug for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Key") .field("raw", &self.as_raw()) .field("fingerprint", &self.fingerprint_raw()) .field("protocol", &self.protocol()) .field("owner_trust", &self.owner_trust()) .field("issuer", &self.issuer_name_raw()) .field("origin", &self.origin()) .field("last_update", &self.last_update()) .field("list_mode", &self.key_list_mode()) .field("has_secret", &self.has_secret()) .field("expired", &self.is_expired()) .field("revoked", &self.is_revoked()) .field("invalid", &self.is_invalid()) .field("disabled", &self.is_disabled()) .field("can_sign", &self.can_sign()) .field("can_encrypt", &self.can_encrypt()) .field("can_certify", &self.can_certify()) .field("can_auth", &self.can_authenticate()) .field("user_ids", &self.user_ids()) .field("subkeys", &self.subkeys()) .finish() } } /// Upstream documentation: [`gpgme_subkey_t`](https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#index-gpgme_005fsubkey_005ft) #[derive(Copy, Clone)] pub struct Subkey<'key>(NonNull, PhantomData<&'key Key>); unsafe impl Send for Subkey<'_> {} unsafe impl Sync for Subkey<'_> {} impl<'key> Subkey<'key> { impl_wrapper!(ffi::gpgme_subkey_t, PhantomData); #[inline] pub fn id(&self) -> Result<&'key str, Option> { self.id_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn id_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).keyid.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn fingerprint(&self) -> Result<&'key str, Option> { self.fingerprint_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn fingerprint_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).fpr.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn creation_time(&self) -> Option { let timestamp = unsafe { (*self.as_raw()).timestamp }; if timestamp > 0 { Some(UNIX_EPOCH + Duration::from_secs(timestamp as u64)) } else { None } } #[inline] pub fn expiration_time(&self) -> Option { let expires = unsafe { (*self.as_raw()).expires }; if expires > 0 { Some(UNIX_EPOCH + Duration::from_secs(expires as u64)) } else { None } } #[inline] pub fn never_expires(&self) -> bool { self.expiration_time().is_none() } #[inline] pub fn is_bad(&self) -> bool { self.is_revoked() || self.is_expired() || self.is_disabled() || self.is_invalid() } #[inline] pub fn is_revoked(&self) -> bool { unsafe { (*self.as_raw()).revoked() } } #[inline] pub fn is_expired(&self) -> bool { unsafe { (*self.as_raw()).expired() } } #[inline] pub fn is_invalid(&self) -> bool { unsafe { (*self.as_raw()).invalid() } } #[inline] pub fn is_disabled(&self) -> bool { unsafe { (*self.as_raw()).disabled() } } #[inline] pub fn can_encrypt(&self) -> bool { unsafe { (*self.as_raw()).can_encrypt() } } #[inline] pub fn can_sign(&self) -> bool { unsafe { (*self.as_raw()).can_sign() } } #[inline] pub fn can_certify(&self) -> bool { unsafe { (*self.as_raw()).can_certify() } } #[inline] pub fn can_authenticate(&self) -> bool { unsafe { (*self.as_raw()).can_authenticate() } } #[inline] pub fn is_qualified(&self) -> bool { unsafe { (*self.as_raw()).is_qualified() } } #[inline] pub fn is_card_key(&self) -> bool { unsafe { (*self.as_raw()).is_cardkey() } } #[inline] pub fn is_secret(&self) -> bool { unsafe { (*self.as_raw()).secret() } } #[inline] pub fn is_de_vs(&self) -> bool { unsafe { (*self.as_raw()).is_de_vs() } } #[inline] pub fn algorithm(&self) -> KeyAlgorithm { unsafe { KeyAlgorithm::from_raw((*self.as_raw()).pubkey_algo) } } /// Upstream documentation: [`gpgme_pubkey_algo_string`](https://www.gnupg.org/documentation/manuals/gpgme/Public-Key-Algorithms.html#index-gpgme_005fpubkey_005falgo_005fstring) #[inline] pub fn algorithm_name(&self) -> Result { unsafe { match ffi::gpgme_pubkey_algo_string(self.as_raw()).as_mut() { Some(raw) => { let result = CStr::from_ptr(raw) .to_str() .expect("algorithm name is not valid utf-8") .to_owned(); ffi::gpgme_free(raw as *mut _ as *mut _); Ok(result) } None => Err(Error::last_os_error()), } } } #[inline] pub fn keygrip(&self) -> Result<&'key str, Option> { self.keygrip_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn keygrip_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).keygrip.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn length(&self) -> usize { unsafe { (*self.as_raw()).length as usize } } #[inline] pub fn card_serial_number(&self) -> Result<&'key str, Option> { self.card_serial_number_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn card_serial_number_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()) .card_number .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn curve(&self) -> Result<&'key str, Option> { self.curve_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn curve_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).curve.as_ref().map(|s| CStr::from_ptr(s)) } } } impl fmt::Debug for Subkey<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Subkey") .field("raw", &self.as_raw()) .field("fingerprint", &self.fingerprint_raw()) .field("secret", &self.is_secret()) .field("algorithm", &self.algorithm()) .field("expired", &self.is_expired()) .field("creation_time", &self.creation_time()) .field("expiration_time", &self.expiration_time()) .field("curve", &self.curve_raw()) .field("length", &self.length()) .field("card_key", &self.is_card_key()) .field("card_serial_number", &self.card_serial_number_raw()) .field("revoked", &self.is_revoked()) .field("invalid", &self.is_invalid()) .field("disabled", &self.is_disabled()) .field("can_sign", &self.can_sign()) .field("can_encrypt", &self.can_encrypt()) .field("can_certify", &self.can_certify()) .field("can_auth", &self.can_authenticate()) .finish() } } impl_list_iterator!(pub struct Subkeys(Subkey: ffi::gpgme_subkey_t)); /// Upstream documentation: [`gpgme_user_id_t`](https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#index-gpgme_005fuser_005fid_005ft) #[derive(Copy, Clone)] pub struct UserId<'key>(NonNull, PhantomData<&'key Key>); unsafe impl Send for UserId<'_> {} unsafe impl Sync for UserId<'_> {} impl<'key> UserId<'key> { impl_wrapper!(ffi::gpgme_user_id_t, PhantomData); #[inline] pub fn id(&self) -> Result<&'key str, Option> { self.id_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn id_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).uid.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn name(&self) -> Result<&'key str, Option> { self.name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn name_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).name.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn email(&self) -> Result<&'key str, Option> { self.email_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn email_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).email.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn comment(&self) -> Result<&'key str, Option> { self.comment_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn comment_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).comment.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] #[cfg(feature = "v1_14")] pub fn uidhash(&self) -> Result<&'key str, Option> { self.uidhash_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] #[cfg(feature = "v1_14")] pub fn uidhash_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).uidhash.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn address(&self) -> Result<&'key str, Option> { self.address_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn address_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).address.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn validity(&self) -> Validity { unsafe { Validity::from_raw((*self.as_raw()).validity) } } #[inline] pub fn is_bad(&self) -> bool { self.is_revoked() || self.is_invalid() } #[inline] pub fn is_revoked(&self) -> bool { unsafe { (*self.as_raw()).revoked() } } #[inline] pub fn is_invalid(&self) -> bool { unsafe { (*self.as_raw()).invalid() } } #[inline] pub fn origin(&self) -> crate::KeyOrigin { unsafe { crate::KeyOrigin::from_raw((*self.as_raw()).origin()) } } #[inline] pub fn last_update(&self) -> SystemTime { let timestamp = unsafe { (*self.as_raw()).last_update }; UNIX_EPOCH + Duration::from_secs(timestamp.into()) } #[inline] pub fn signature(&self, key: &Key) -> Option> { if key.protocol() != Protocol::OpenPgp { return None; } self.signatures() .filter(|s| { s.signer_key_id_raw() == key.id_raw() && !(s.is_bad() || s.is_revocation()) && (s.status() == Error::NO_ERROR) }) .max_by_key(|s| s.creation_time()) } #[inline] pub fn signatures(&self) -> UserIdSignatures<'key> { unsafe { UserIdSignatures::from_list((*self.as_raw()).signatures) } } #[inline] pub fn tofu_info(&self) -> Option> { unsafe { (*self.as_raw()) .tofu .as_mut() .map(|t| crate::TofuInfo::from_raw(t)) } } } impl fmt::Debug for UserId<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("UserId") .field("raw", &self.as_raw()) .field("name", &self.name_raw()) .field("email", &self.email_raw()) .field("comment", &self.comment_raw()) .field("validity", &self.validity()) .field("revoked", &self.is_revoked()) .field("invalid", &self.is_invalid()) .field("origin", &self.origin()) .field("tofu_info", &self.tofu_info()) .field("signatures", &self.signatures()) .finish() } } impl fmt::Display for UserId<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str( &*self .id_raw() .map(|s| s.to_string_lossy()) .unwrap_or("".into()), ) } } impl_list_iterator!(pub struct UserIds(UserId: ffi::gpgme_user_id_t)); #[derive(Debug)] pub enum SignatureTrust { None, Partial, Complete, } /// Upstream documentation: [`gpgme_key_sig_t`](https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#index-gpgme_005fkey_005fsig_005ft) #[derive(Copy, Clone)] pub struct UserIdSignature<'key>(NonNull, PhantomData<&'key Key>); unsafe impl Send for UserIdSignature<'_> {} unsafe impl Sync for UserIdSignature<'_> {} impl<'key> UserIdSignature<'key> { impl_wrapper!(ffi::gpgme_key_sig_t, PhantomData); #[inline] pub fn signer_key_id(&self) -> Result<&'key str, Option> { self.signer_key_id_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn signer_key_id_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).keyid.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn algorithm(&self) -> KeyAlgorithm { unsafe { KeyAlgorithm::from_raw((*self.as_raw()).pubkey_algo) } } #[inline] pub fn creation_time(&self) -> Option { let timestamp = unsafe { (*self.as_raw()).timestamp }; if timestamp > 0 { Some(UNIX_EPOCH + Duration::from_secs(timestamp as u64)) } else { None } } #[inline] pub fn expiration_time(&self) -> Option { let expires = unsafe { (*self.as_raw()).expires }; if expires > 0 { Some(UNIX_EPOCH + Duration::from_secs(expires as u64)) } else { None } } #[inline] pub fn never_expires(&self) -> bool { self.expiration_time().is_none() } #[inline] pub fn is_bad(&self) -> bool { self.is_expired() || self.is_invalid() } #[inline] pub fn is_revocation(&self) -> bool { unsafe { (*self.as_raw()).revoked() } } #[inline] pub fn is_invalid(&self) -> bool { unsafe { (*self.as_raw()).invalid() } } #[inline] pub fn is_expired(&self) -> bool { unsafe { (*self.as_raw()).expired() } } #[inline] pub fn is_exportable(&self) -> bool { unsafe { (*self.as_raw()).exportable() } } #[inline] pub fn signer_user_id(&self) -> Result<&'key str, Option> { self.signer_user_id_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn signer_user_id_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).uid.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn signer_name(&self) -> Result<&'key str, Option> { self.signer_name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn signer_name_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).name.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn signer_email(&self) -> Result<&'key str, Option> { self.signer_email_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn signer_email_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).email.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn signer_comment(&self) -> Result<&'key str, Option> { self.signer_comment_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn signer_comment_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()).comment.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn cert_class(&self) -> u64 { unsafe { (*self.as_raw()).sig_class.into() } } #[inline] pub fn status(&self) -> Error { unsafe { Error::new((*self.as_raw()).status) } } #[inline] pub fn policy_url(&self) -> Result<&'key str, Option> { self.policy_url_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn policy_url_raw(&self) -> Option<&'key CStr> { self.notations().find_map(|n| { if n.name_raw().is_none() { n.value_raw() } else { None } }) } #[inline] pub fn remark(&self) -> Result<&'key str, Option> { self.remark_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn remark_raw(&self) -> Option<&'key CStr> { self.notations().find_map(|n| { if n.name() == Ok("rem@gnupg.org") { n.value_raw() } else { None } }) } #[inline] pub fn notations(&self) -> SignatureNotations<'key> { unsafe { SignatureNotations::from_list((*self.as_raw()).notations) } } #[inline] #[cfg(feature = "v1_16")] pub fn is_trust_signature(&self) -> bool { self.trust_depth() != 0 } #[inline] #[cfg(feature = "v1_16")] pub fn trust_value(&self) -> SignatureTrust { let value = unsafe { (*self.as_raw()).trust_value() }; if !self.is_trust_signature() { SignatureTrust::None } else if value >= 120 { SignatureTrust::Complete } else { SignatureTrust::Partial } } #[inline] #[cfg(feature = "v1_16")] pub fn trust_depth(&self) -> u8 { unsafe { (*self.as_raw()).trust_depth() } } #[inline] #[cfg(feature = "v1_16")] pub fn trust_scope(&self) -> Result<&'key str, Option> { self.trust_scope_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] #[cfg(feature = "v1_16")] pub fn trust_scope_raw(&self) -> Option<&'key CStr> { unsafe { (*self.as_raw()) .trust_scope .as_ref() .map(|s| CStr::from_ptr(s)) } } } impl fmt::Debug for UserIdSignature<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("UserIdSignature") .field("raw", &self.as_raw()) .field("signer_key", &self.signer_key_id_raw()) .field("signer", &self.signer_user_id_raw()) .field("algorithm", &self.algorithm()) .field("expired", &self.is_expired()) .field("creation_time", &self.creation_time()) .field("expiration_time", &self.expiration_time()) .field("invalid", &self.is_invalid()) .field("revoked", &self.is_revocation()) .field("exportable", &self.is_exportable()) .field("status", &self.status()) .field("notations", &self.notations()) .finish() } } impl_list_iterator!(pub struct UserIdSignatures(UserIdSignature: ffi::gpgme_key_sig_t)); gpgme-0.11.0/src/lib.rs000064400000000000000000000302371046102023000127120ustar 00000000000000#![forbid(missing_debug_implementations)] use std::{ ffi::CStr, fmt, ptr, str::Utf8Error, sync::{Mutex, RwLock}, }; use self::{engine::EngineInfoGuard, error::return_err, utils::CStrArgument}; use once_cell::sync::Lazy; #[doc(inline)] #[allow(deprecated)] pub use self::{ callbacks::{ EditInteractionStatus, EditInteractor, InteractionStatus, Interactor, PassphraseProvider, PassphraseRequest, ProgressInfo, ProgressReporter, StatusHandler, }, context::{Context, ContextWithCallbacks}, data::{Data, IntoData}, engine::EngineInfo, error::{Error, Result}, flags::*, keys::{Key, Subkey, UserId, UserIdSignature}, notation::SignatureNotation, results::{ DecryptionResult, EncryptionResult, Import, ImportResult, InvalidKey, KeyGenerationResult, KeyListResult, NewSignature, PkaTrust, QuerySwdbResult, Recipient, Signature, SigningResult, VerificationResult, }, tofu::{TofuInfo, TofuPolicy}, }; #[doc(inline)] pub use gpg_error as error; #[macro_use] mod utils; mod callbacks; pub mod context; pub mod data; pub mod edit; pub mod engine; mod flags; pub mod keys; pub mod notation; pub mod results; pub mod tofu; ffi_enum_wrapper! { /// A cryptographic protocol that may be used with the library. /// /// Each protocol is implemented by an engine that the library communicates with /// to perform various operations. /// /// Upstream documentation: /// [`gpgme_protocol_t`](https://www.gnupg.org/documentation/manuals/gpgme/Protocols-and-Engines.html#index-enum-gpgme_005fprotocol_005ft) #[non_exhaustive] pub enum Protocol: ffi::gpgme_protocol_t { OpenPgp = ffi::GPGME_PROTOCOL_OpenPGP, Cms = ffi::GPGME_PROTOCOL_CMS, GpgConf = ffi::GPGME_PROTOCOL_GPGCONF, Assuan = ffi::GPGME_PROTOCOL_ASSUAN, G13 = ffi::GPGME_PROTOCOL_G13, UiServer = ffi::GPGME_PROTOCOL_UISERVER, Spawn = ffi::GPGME_PROTOCOL_SPAWN, Default = ffi::GPGME_PROTOCOL_DEFAULT, Unknown = ffi::GPGME_PROTOCOL_UNKNOWN, } } impl Protocol { /// Upstream documentation: /// [`gpgme_get_protocol_name`](https://www.gnupg.org/documentation/manuals/gpgme/Protocols-and-Engines.html#index-gpgme_005fget_005fprotocol_005fname) #[inline] pub fn name(&self) -> Result<&'static str, Option> { self.name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Upstream documentation: /// [`gpgme_get_protocol_name`](https://www.gnupg.org/documentation/manuals/gpgme/Protocols-and-Engines.html#index-gpgme_005fget_005fprotocol_005fname) #[inline] pub fn name_raw(&self) -> Option<&'static CStr> { unsafe { ffi::gpgme_get_protocol_name(self.raw()) .as_ref() .map(|s| CStr::from_ptr(s)) } } } impl fmt::Display for Protocol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.name().unwrap_or("Unknown")) } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_validity_t`](https://www.gnupg.org/documentation/manuals/gpgme/Information-About-Keys.html#index-gpgme_005fvalidity_005ft) pub enum Validity(Unknown): ffi::gpgme_validity_t { Unknown = ffi::GPGME_VALIDITY_UNKNOWN, Undefined = ffi::GPGME_VALIDITY_UNDEFINED, Never = ffi::GPGME_VALIDITY_NEVER, Marginal = ffi::GPGME_VALIDITY_MARGINAL, Full = ffi::GPGME_VALIDITY_FULL, Ultimate = ffi::GPGME_VALIDITY_ULTIMATE, } } impl fmt::Display for Validity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Validity::Undefined => write!(f, "q"), Validity::Never => write!(f, "n"), Validity::Marginal => write!(f, "m"), Validity::Full => write!(f, "f"), Validity::Ultimate => write!(f, "u"), _ => write!(f, "?"), } } } static FLAG_LOCK: Lazy> = Lazy::new(|| Mutex::default()); /// Upstream documentation: /// [`gpgme_set_global_flag`](https://www.gnupg.org/documentation/manuals/gpgme/Library-Version-Check.html#index-gpgme_005fset_005fglobal_005fflag) pub fn set_flag(name: impl CStrArgument, val: impl CStrArgument) -> Result<()> { let name = name.into_cstr(); let val = val.into_cstr(); let _lock = FLAG_LOCK.lock().unwrap_or_else(|e| e.into_inner()); unsafe { if ffi::gpgme_set_global_flag(name.as_ref().as_ptr(), val.as_ref().as_ptr()) == 0 { Ok(()) } else { Err(Error::GENERAL) } } } cfg_if::cfg_if! { if #[cfg(feature = "v1_18")] { const MIN_VERSION: &[u8] = b"1.18.0\0"; } else if #[cfg(feature = "v1_17")] { const MIN_VERSION: &[u8] = b"1.17.0\0"; } else if #[cfg(feature = "v1_16")] { const MIN_VERSION: &[u8] = b"1.16.0\0"; } else if #[cfg(feature = "v1_15")] { const MIN_VERSION: &[u8] = b"1.15.0\0"; } else if #[cfg(feature = "v1_14")] { const MIN_VERSION: &[u8] = b"1.14.0\0"; } else { const MIN_VERSION: &[u8] = b"1.13.0\0"; } } /// Initializes the gpgme library. /// /// Upstream documentation: /// [`gpgme_check_version`](https://www.gnupg.org/documentation/manuals/gpgme/Library-Version-Check.html#index-gpgme_005fcheck_005fversion) /// /// # Examples /// /// ```no_run /// let gpgme = gpgme::init(); /// ``` #[inline] pub fn init() -> Gpgme { static TOKEN: Lazy<(&str, RwLock<()>)> = Lazy::new(|| unsafe { let offset = memoffset::offset_of!(ffi::_gpgme_signature, validity); let result = ffi::gpgme_check_version_internal(MIN_VERSION.as_ptr().cast(), offset); assert!( !result.is_null(), "the library linked is not the correct version" ); ( CStr::from_ptr(result) .to_str() .expect("gpgme version string is not valid utf-8"), RwLock::default(), ) }); Gpgme { version: TOKEN.0, engine_lock: &TOKEN.1, } } /// A type for managing the library's configuration. #[derive(Debug, Clone)] pub struct Gpgme { version: &'static str, engine_lock: &'static RwLock<()>, } impl Gpgme { pub const HOME_DIR: &'static str = "homedir"; pub const AGENT_SOCKET: &'static str = "agent-socket"; pub const UISERVER_SOCKET: &'static str = "uiserver-socket"; pub const GPGCONF_NAME: &'static str = "gpgconf-name"; pub const GPG_NAME: &'static str = "gpg-name"; pub const GPGSM_NAME: &'static str = "gpgsm-name"; pub const G13_NAME: &'static str = "g13-name"; /// Checks that the linked version of the library is at least the /// specified version. /// /// Note: `false` is returned, if `version` is not in the format `MAJOR.MINOR.MICRO`. /// /// Upstream documentation: /// [`gpgme_check_version`](https://www.gnupg.org/documentation/manuals/gpgme/Library-Version-Check.html#index-gpgme_005fcheck_005fversion) /// /// # Examples /// /// ```no_run /// let gpgme = gpgme::init(); /// assert!(gpgme.check_version("1.4.0")); /// ``` #[inline] pub fn check_version(&self, version: impl CStrArgument) -> bool { let version = version.into_cstr(); unsafe { !ffi::gpgme_check_version(version.as_ref().as_ptr()).is_null() } } /// Returns the version string for the library. #[inline] pub fn version(&self) -> &'static str { self.version } /// Returns the default value for specified configuration option. /// /// Commonly supported values for `what` are provided as associated constants. /// /// Upstream documentation: /// [`gpgme_get_dirinfo`](https://www.gnupg.org/documentation/manuals/gpgme/Engine-Version-Check.html#index-gpgme_005fget_005fdirinfo) #[inline] pub fn get_dir_info(&self, what: impl CStrArgument) -> Result<&'static str, Option> { self.get_dir_info_raw(what) .map_or(Err(None), |s| s.to_str().map_err(Some)) } /// Returns the default value for specified configuration option. /// /// Commonly supported values for `what` are provided as associated constants. /// /// Upstream documentation: /// [`gpgme_get_dirinfo`](https://www.gnupg.org/documentation/manuals/gpgme/Engine-Version-Check.html#index-gpgme_005fget_005fdirinfo) #[inline] pub fn get_dir_info_raw(&self, what: impl CStrArgument) -> Option<&'static CStr> { let what = what.into_cstr(); unsafe { ffi::gpgme_get_dirinfo(what.as_ref().as_ptr()) .as_ref() .map(|s| CStr::from_ptr(s)) } } /// Checks that the engine implementing the specified protocol is supported by the library. /// /// Upstream documentation: /// [`gpgme_engine_check_version`](https://www.gnupg.org/documentation/manuals/gpgme/Engine-Version-Check.html#index-gpgme_005fengine_005fcheck_005fversion) pub fn check_engine_version(&self, proto: Protocol) -> Result<()> { unsafe { return_err!(ffi::gpgme_engine_check_version(proto.raw())); } Ok(()) } /// Returns an iterator yielding information on each of the globally configured engines. /// /// Upstream documentation: /// [`gpgme_get_engine_info`](https://www.gnupg.org/documentation/manuals/gpgme/Engine-Information.html#index-gpgme_005fget_005fengine_005finfo) #[inline] pub fn engine_info(&self) -> Result { EngineInfoGuard::new(self.engine_lock) } // Requires the engine_lock to be held by the current thread when called unsafe fn get_engine_info(&self, proto: Protocol) -> ffi::gpgme_engine_info_t { let mut info = ptr::null_mut(); assert_eq!(ffi::gpgme_get_engine_info(&mut info), 0); while !info.is_null() && ((*info).protocol != proto.raw()) { info = (*info).next; } info } #[inline] pub fn set_engine_path(&self, proto: Protocol, path: impl CStrArgument) -> Result<()> { let path = path.into_cstr(); let _lock = self .engine_lock .write() .expect("engine info lock was poisoned"); unsafe { let home_dir = self .get_engine_info(proto) .as_ref() .map_or(ptr::null(), |e| (*e).home_dir); return_err!(ffi::gpgme_set_engine_info( proto.raw(), path.as_ref().as_ptr(), home_dir, )); } Ok(()) } #[inline] pub fn set_engine_home_dir(&self, proto: Protocol, home_dir: impl CStrArgument) -> Result<()> { let home_dir = home_dir.into_cstr(); let _lock = self .engine_lock .write() .expect("engine info lock was poisoned"); unsafe { let path = self .get_engine_info(proto) .as_ref() .map_or(ptr::null(), |e| (*e).file_name); return_err!(ffi::gpgme_set_engine_info( proto.raw(), path, home_dir.as_ref().as_ptr(), )); } Ok(()) } /// Upstream documentation: /// [`gpgme_set_engine_info`](https://www.gnupg.org/documentation/manuals/gpgme/Engine-Configuration.html#index-gpgme_005fset_005fengine_005finfo) #[inline] pub fn set_engine_info( &self, proto: Protocol, path: Option, home_dir: Option, ) -> Result<()> { let path = path.map(CStrArgument::into_cstr); let home_dir = home_dir.map(CStrArgument::into_cstr); let path = path.as_ref().map_or(ptr::null(), |s| s.as_ref().as_ptr()); let home_dir = home_dir .as_ref() .map_or(ptr::null(), |s| s.as_ref().as_ptr()); unsafe { let _lock = self .engine_lock .write() .expect("engine info lock was poisoned"); return_err!(ffi::gpgme_set_engine_info(proto.raw(), path, home_dir)); } Ok(()) } } unsafe trait OpResult: Clone { fn from_context(ctx: &Context) -> Option; } type NonNull = ptr::NonNull<::Inner>; gpgme-0.11.0/src/notation.rs000064400000000000000000000041311046102023000137710ustar 00000000000000use std::{ffi::CStr, fmt, marker::PhantomData, str::Utf8Error}; use ffi; use crate::{NonNull, SignatureNotationFlags}; /// Upstream documentation: /// [`gpgme_sig_notation_t`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fsig_005fnotation_005ft) #[derive(Copy, Clone)] pub struct SignatureNotation<'a>(NonNull, PhantomData<&'a ()>); unsafe impl Send for SignatureNotation<'_> {} unsafe impl Sync for SignatureNotation<'_> {} impl<'a> SignatureNotation<'a> { impl_wrapper!(ffi::gpgme_sig_notation_t, PhantomData); #[inline] pub fn is_human_readable(&self) -> bool { unsafe { (*self.as_raw()).human_readable() } } #[inline] pub fn is_critical(&self) -> bool { unsafe { (*self.as_raw()).critical() } } #[inline] pub fn flags(&self) -> SignatureNotationFlags { unsafe { SignatureNotationFlags::from_bits_truncate((*self.as_raw()).flags) } } #[inline] pub fn name(&self) -> Result<&'a str, Option> { self.name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn name_raw(&self) -> Option<&'a CStr> { unsafe { (*self.as_raw()).name.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn value(&self) -> Result<&'a str, Option> { self.value_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn value_raw(&self) -> Option<&'a CStr> { unsafe { (*self.as_raw()).value.as_ref().map(|s| CStr::from_ptr(s)) } } } impl fmt::Debug for SignatureNotation<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SignatureNotation") .field("raw", &self.as_raw()) .field("name", &self.name_raw()) .field("value", &self.value_raw()) .field("critical", &self.is_critical()) .field("human_readable", &self.is_human_readable()) .finish() } } impl_list_iterator!(pub struct SignatureNotations(SignatureNotation: ffi::gpgme_sig_notation_t)); gpgme-0.11.0/src/results.rs000064400000000000000000000654031046102023000136500ustar 00000000000000#![allow(trivial_numeric_casts)] use std::{ ffi::CStr, fmt, marker::PhantomData, ptr, str::Utf8Error, time::{Duration, SystemTime, UNIX_EPOCH}, }; use ffi; use libc; use crate::{ error::return_err, notation::SignatureNotations, Context, Error, HashAlgorithm, ImportFlags, KeyAlgorithm, NonNull, OpResult, Result, SignMode, SignatureSummary, Validity, }; macro_rules! impl_result { ($(#[$Attr:meta])* $Name:ident : $T:ty = $Constructor:expr) => { $(#[$Attr])* pub struct $Name(NonNull<$T>); unsafe impl Send for $Name {} unsafe impl Sync for $Name {} impl Drop for $Name { #[inline] fn drop(&mut self) { unsafe { ffi::gpgme_result_unref(self.as_raw().cast()); } } } impl Clone for $Name { #[inline] fn clone(&self) -> Self { unsafe { ffi::gpgme_result_ref(self.as_raw().cast()); Self::from_raw(self.as_raw()) } } } unsafe impl OpResult for $Name { fn from_context(ctx: &Context) -> Option { unsafe { $Constructor(ctx.as_raw()).as_mut().map(|r| { ffi::gpgme_result_ref(ptr::addr_of_mut!(*r).cast()); Self::from_raw(r) }) } } } impl $Name { impl_wrapper!($T); } }; } macro_rules! impl_subresult { ($(#[$Attr:meta])* $Name:ident : $T:ty, $IterName:ident, $Owner:ty) => { $(#[$Attr])* #[derive(Copy, Clone)] pub struct $Name<'result>(NonNull<$T>, PhantomData<&'result $Owner>); unsafe impl Send for $Name<'_> {} unsafe impl Sync for $Name<'_> {} impl $Name<'_> { impl_wrapper!($T, PhantomData); } impl_list_iterator!(pub struct $IterName($Name: $T)); }; } impl_subresult! { /// Upstream documentation: /// [`gpgme_invalid_key_t`](https://www.gnupg.org/documentation/manuals/gpgme/Crypto-Operations.html#index-gpgme_005finvalid_005fkey_005ft) InvalidKey: ffi::gpgme_invalid_key_t, InvalidKeys, () } impl<'a> InvalidKey<'a> { #[inline] pub fn fingerprint(&self) -> Result<&'a str, Option> { self.fingerprint_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn fingerprint_raw(&self) -> Option<&'a CStr> { unsafe { (*self.as_raw()).fpr.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn reason(&self) -> Option { unsafe { match Error::new((*self.as_raw()).reason) { Error::NO_ERROR => None, e => Some(e), } } } } impl fmt::Debug for InvalidKey<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("InvalidKey") .field("raw", &self.as_raw()) .field("fingerprint", &self.fingerprint_raw()) .field("reason", &self.reason()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_keylist_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Listing-Keys.html#index-gpgme_005fkeylist_005fresult_005ft) KeyListResult: ffi::gpgme_keylist_result_t = ffi::gpgme_op_keylist_result } impl KeyListResult { pub fn is_truncated(&self) -> bool { unsafe { (*self.as_raw()).truncated() } } } impl fmt::Debug for KeyListResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("KeyListResult") .field("raw", &self.as_raw()) .field("truncated", &self.is_truncated()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_genkey_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Generating-Keys.html#index-gpgme_005fgenkey_005fresult_005ft) KeyGenerationResult: ffi::gpgme_genkey_result_t = ffi::gpgme_op_genkey_result } impl KeyGenerationResult { #[inline] pub fn has_primary_key(&self) -> bool { unsafe { (*self.as_raw()).primary() } } #[inline] pub fn has_sub_key(&self) -> bool { unsafe { (*self.as_raw()).sub() } } #[inline] pub fn has_uid(&self) -> bool { unsafe { (*self.as_raw()).uid() } } #[inline] pub fn fingerprint(&self) -> Result<&str, Option> { self.fingerprint_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn fingerprint_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()).fpr.as_ref().map(|s| CStr::from_ptr(s)) } } } impl fmt::Debug for KeyGenerationResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("KeyGenerationResult") .field("raw", &self.as_raw()) .field("fingerprint", &self.fingerprint_raw()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_import_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html#index-gpgme_005fimport_005fresult_005ft) ImportResult: ffi::gpgme_import_result_t = ffi::gpgme_op_import_result } impl ImportResult { #[inline] pub fn considered(&self) -> u32 { unsafe { (*self.as_raw()).considered as u32 } } #[inline] pub fn without_user_id(&self) -> u32 { unsafe { (*self.as_raw()).no_user_id as u32 } } #[inline] pub fn imported(&self) -> u32 { unsafe { (*self.as_raw()).imported as u32 } } #[inline] pub fn imported_rsa(&self) -> u32 { unsafe { (*self.as_raw()).imported_rsa as u32 } } #[inline] pub fn unchanged(&self) -> u32 { unsafe { (*self.as_raw()).unchanged as u32 } } #[inline] pub fn new_user_ids(&self) -> u32 { unsafe { (*self.as_raw()).new_user_ids as u32 } } #[inline] pub fn new_subkeys(&self) -> u32 { unsafe { (*self.as_raw()).new_sub_keys as u32 } } #[inline] pub fn new_signatures(&self) -> u32 { unsafe { (*self.as_raw()).new_signatures as u32 } } #[inline] pub fn new_revocations(&self) -> u32 { unsafe { (*self.as_raw()).new_revocations as u32 } } #[inline] pub fn secret_considered(&self) -> u32 { unsafe { (*self.as_raw()).secret_read as u32 } } #[inline] pub fn secret_imported(&self) -> u32 { unsafe { (*self.as_raw()).secret_imported as u32 } } #[inline] pub fn secret_unchanged(&self) -> u32 { unsafe { (*self.as_raw()).secret_unchanged as u32 } } #[inline] pub fn not_imported(&self) -> u32 { unsafe { (*self.as_raw()).not_imported as u32 } } #[inline] pub fn skipped_v3_keys(&self) -> u32 { unsafe { (*self.as_raw()).skipped_v3_keys as u32 } } #[inline] pub fn imports(&self) -> Imports<'_> { unsafe { Imports::from_list((*self.as_raw()).imports) } } } impl fmt::Debug for ImportResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ImportResult") .field("raw", &self.as_raw()) .field("without_user_id", &self.without_user_id()) .field("new_user_ids", &self.new_user_ids()) .field("new_subkeys", &self.new_subkeys()) .field("new_signatures", &self.new_signatures()) .field("new_revocations", &self.new_revocations()) .field("considered", &self.considered()) .field("imported", &self.imported()) .field("unchanged", &self.unchanged()) .field("secret_considered", &self.secret_considered()) .field("secret_imported", &self.secret_imported()) .field("secret_unchanged", &self.secret_unchanged()) .field("not_imported", &self.not_imported()) .field("imports", &self.imports()) .finish() } } impl_subresult! { /// Upstream documentation: /// [`gpgme_import_status_t`](https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html#index-gpgme_005fimport_005fstatus_005ft) Import: ffi::gpgme_import_status_t, Imports, ImportResult } impl<'result> Import<'result> { #[inline] pub fn fingerprint(&self) -> Result<&'result str, Option> { self.fingerprint_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn fingerprint_raw(&self) -> Option<&'result CStr> { unsafe { (*self.as_raw()).fpr.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn result(&self) -> Result<()> { unsafe { return_err!((*self.as_raw()).result); Ok(()) } } #[inline] pub fn status(&self) -> ImportFlags { unsafe { ImportFlags::from_bits_truncate((*self.as_raw()).status) } } } impl fmt::Debug for Import<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Import") .field("raw", &self.as_raw()) .field("fingerprint", &self.fingerprint_raw()) .field("result", &self.result()) .field("status", &self.status()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_encrypt_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Encrypting-a-Plaintext.html#index-gpgme_005fencrypt_005fresult_005ft) EncryptionResult: ffi::gpgme_encrypt_result_t = ffi::gpgme_op_encrypt_result } impl EncryptionResult { #[inline] pub fn invalid_recipients(&self) -> InvalidKeys<'_> { unsafe { InvalidKeys::from_list((*self.as_raw()).invalid_recipients) } } } impl fmt::Debug for EncryptionResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EncryptionResult") .field("raw", &self.as_raw()) .field("invalid_recipients", &self.invalid_recipients()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_decrypt_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Decrypt.html#index-gpgme_005fdecrypt_005fresult_005ft) DecryptionResult: ffi::gpgme_decrypt_result_t = ffi::gpgme_op_decrypt_result } impl DecryptionResult { #[inline] pub fn unsupported_algorithm(&self) -> Result<&str, Option> { self.unsupported_algorithm_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn unsupported_algorithm_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .unsupported_algorithm .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn is_wrong_key_usage(&self) -> bool { unsafe { (*self.as_raw()).wrong_key_usage() } } #[inline] pub fn is_de_vs(&self) -> bool { unsafe { (*self.as_raw()).is_de_vs() } } #[inline] pub fn is_mime(&self) -> bool { unsafe { (*self.as_raw()).is_mime() } } #[inline] pub fn is_legacy_cipher_no_mdc(&self) -> bool { unsafe { (*self.as_raw()).legacy_cipher_nomdc() } } #[inline] pub fn filename(&self) -> Result<&str, Option> { self.filename_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn filename_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .file_name .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn symmetric_key_algorithm(&self) -> Result<&str, Option> { self.symmetric_key_algorithm_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn symmetric_key_algorithm_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .symkey_algo .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn recipients(&self) -> Recipients<'_> { unsafe { Recipients::from_list((*self.as_raw()).recipients) } } } impl fmt::Debug for DecryptionResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DecryptionResult") .field("raw", &self.as_raw()) .field("unsupported_algorithm", &self.unsupported_algorithm_raw()) .field("wrong_key_usage", &self.is_wrong_key_usage()) .field("filename", &self.filename_raw()) .field("recipients", &self.recipients()) .finish() } } impl_subresult! { /// Upstream documentation: /// [`gpgme_recipient_t`](https://www.gnupg.org/documentation/manuals/gpgme/Decrypt.html#index-gpgme_005frecipient_005ft) Recipient: ffi::gpgme_recipient_t, Recipients, DecryptionResult } impl<'result> Recipient<'result> { #[inline] pub fn key_id(&self) -> Result<&'result str, Option> { self.key_id_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn key_id_raw(&self) -> Option<&'result CStr> { unsafe { (*self.as_raw()).keyid.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn algorithm(&self) -> KeyAlgorithm { unsafe { KeyAlgorithm::from_raw((*self.as_raw()).pubkey_algo) } } #[inline] pub fn status(&self) -> Result<()> { unsafe { return_err!((*self.as_raw()).status); Ok(()) } } } impl fmt::Debug for Recipient<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Recipient") .field("raw", &self.as_raw()) .field("key_id", &self.key_id_raw()) .field("algorithm", &self.algorithm()) .field("status", &self.status()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_sign_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Creating-a-Signature.html#index-gpgme_005fsign_005fresult_005ft) SigningResult: ffi::gpgme_sign_result_t = ffi::gpgme_op_sign_result } impl SigningResult { #[inline] pub fn invalid_signers(&self) -> InvalidKeys<'_> { unsafe { InvalidKeys::from_list((*self.as_raw()).invalid_signers) } } #[inline] pub fn new_signatures(&self) -> NewSignatures<'_> { unsafe { NewSignatures::from_list((*self.as_raw()).signatures) } } } impl fmt::Debug for SigningResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SigningResult") .field("raw", &self.as_raw()) .field("invalid_signers", &self.invalid_signers()) .field("new_signatures", &self.new_signatures()) .finish() } } impl_subresult! { /// Upstream documentation: /// [`gpgme_new_signature_t`](https://www.gnupg.org/documentation/manuals/gpgme/Creating-a-Signature.html#index-gpgme_005fnew_005fsignature_005ft) NewSignature: ffi::gpgme_new_signature_t, NewSignatures, SigningResult } impl<'result> NewSignature<'result> { #[inline] pub fn fingerprint(&self) -> Result<&'result str, Option> { self.fingerprint_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn fingerprint_raw(&self) -> Option<&'result CStr> { unsafe { (*self.as_raw()).fpr.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn creation_time(&self) -> SystemTime { let timestamp = unsafe { (*self.as_raw()).timestamp }; UNIX_EPOCH + Duration::from_secs(timestamp as u64) } #[inline] pub fn mode(&self) -> SignMode { unsafe { SignMode::from_raw((*self.as_raw()).typ) } } #[inline] pub fn key_algorithm(&self) -> KeyAlgorithm { unsafe { KeyAlgorithm::from_raw((*self.as_raw()).pubkey_algo) } } #[inline] pub fn hash_algorithm(&self) -> HashAlgorithm { unsafe { HashAlgorithm::from_raw((*self.as_raw()).hash_algo) } } #[inline] pub fn signature_class(&self) -> u32 { unsafe { (*self.as_raw()).sig_class.into() } } } impl fmt::Debug for NewSignature<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("NewSignature") .field("raw", &self.as_raw()) .field("fingerprint", &self.fingerprint_raw()) .field("creation_time", &self.creation_time()) .field("mode", &self.mode()) .field("key_algorithm", &self.key_algorithm()) .field("hash_algorithm", &self.hash_algorithm()) .field("class", &self.signature_class()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_verify_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fverify_005fresult_005ft) VerificationResult: ffi::gpgme_verify_result_t = ffi::gpgme_op_verify_result } impl VerificationResult { #[inline] pub fn is_mime(&self) -> bool { unsafe { (*self.as_raw()).is_mime() } } #[inline] pub fn filename(&self) -> Result<&str, Option> { self.filename_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn filename_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .file_name .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn signatures(&self) -> Signatures<'_> { unsafe { Signatures::from_list((*self.as_raw()).signatures) } } } impl fmt::Debug for VerificationResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("VerificationResult") .field("raw", &self.as_raw()) .field("filename", &self.filename_raw()) .field("signatures", &self.signatures()) .finish() } } ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_signature_t`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fsignature_005ft) pub enum PkaTrust: libc::c_uint { Unknown = 0, Bad = 1, Okay = 2, } } impl_subresult! { /// Upstream documentation: /// [`gpgme_signature_t`](https://www.gnupg.org/documentation/manuals/gpgme/Verify.html#index-gpgme_005fsignature_005ft) Signature: ffi::gpgme_signature_t, Signatures, VerificationResult } impl<'result> Signature<'result> { #[inline] pub fn summary(&self) -> SignatureSummary { unsafe { SignatureSummary::from_bits_truncate((*self.as_raw()).summary as u32) } } #[inline] pub fn fingerprint(&self) -> Result<&'result str, Option> { self.fingerprint_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn fingerprint_raw(&self) -> Option<&'result CStr> { unsafe { (*self.as_raw()).fpr.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn status(&self) -> Result<()> { unsafe { return_err!((*self.as_raw()).status); Ok(()) } } #[inline] pub fn creation_time(&self) -> Option { let timestamp = unsafe { (*self.as_raw()).timestamp }; if timestamp > 0 { Some(UNIX_EPOCH + Duration::from_secs(timestamp.into())) } else { None } } #[inline] pub fn expiration_time(&self) -> Option { let expires = unsafe { (*self.as_raw()).exp_timestamp }; if expires > 0 { Some(UNIX_EPOCH + Duration::from_secs(expires.into())) } else { None } } #[inline] pub fn never_expires(&self) -> bool { self.expiration_time().is_none() } #[inline] pub fn is_wrong_key_usage(&self) -> bool { unsafe { (*self.as_raw()).wrong_key_usage() } } #[inline] pub fn verified_by_chain(&self) -> bool { unsafe { (*self.as_raw()).chain_model() } } #[inline] pub fn is_de_vs(&self) -> bool { unsafe { (*self.as_raw()).is_de_vs() } } #[inline] pub fn pka_trust(&self) -> PkaTrust { unsafe { PkaTrust::from_raw((*self.as_raw()).pka_trust()) } } #[inline] pub fn pka_address(&self) -> Result<&'result str, Option> { self.pka_address_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn pka_address_raw(&self) -> Option<&'result CStr> { unsafe { (*self.as_raw()) .pka_address .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn validity(&self) -> Validity { unsafe { Validity::from_raw((*self.as_raw()).validity) } } #[inline] pub fn nonvalidity_reason(&self) -> Option { unsafe { match Error::new((*self.as_raw()).validity_reason) { Error::NO_ERROR => None, e => Some(e), } } } #[inline] pub fn key_algorithm(&self) -> KeyAlgorithm { unsafe { KeyAlgorithm::from_raw((*self.as_raw()).pubkey_algo) } } #[inline] pub fn hash_algorithm(&self) -> HashAlgorithm { unsafe { HashAlgorithm::from_raw((*self.as_raw()).hash_algo) } } #[inline] pub fn policy_url(&self) -> Result<&'result str, Option> { self.policy_url_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn policy_url_raw(&self) -> Option<&'result CStr> { unsafe { let mut notation = (*self.as_raw()).notations; while !notation.is_null() { if (*notation).name.is_null() { return (*notation).value.as_ref().map(|s| CStr::from_ptr(s)); } notation = (*notation).next; } None } } #[inline] pub fn notations(&self) -> SignatureNotations<'result> { unsafe { SignatureNotations::from_list((*self.as_raw()).notations) } } #[inline] pub fn key(&self) -> Option { unsafe { (*self.as_raw()).key.as_mut().map(|k| { ffi::gpgme_key_ref(k); crate::Key::from_raw(k) }) } } } impl fmt::Debug for Signature<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Signature") .field("raw", &self.as_raw()) .field("fingerprint", &self.fingerprint_raw()) .field("creation_time", &self.creation_time()) .field("expiration_time", &self.expiration_time()) .field("key_algorithm", &self.key_algorithm()) .field("hash_algorithm", &self.hash_algorithm()) .field("summary", &self.summary()) .field("status", &self.status()) .field("validity", &self.validity()) .field("nonvalidity_reason", &self.nonvalidity_reason()) .field("notations", &self.notations()) .finish() } } impl_result! { /// Upstream documentation: /// [`gpgme_query_swdb_result_t`](https://www.gnupg.org/documentation/manuals/gpgme/Checking-for-updates.html#index-gpgme_005fquery_005fswdb_005fresult_005ft) QuerySwdbResult: ffi::gpgme_query_swdb_result_t = ffi::gpgme_op_query_swdb_result } impl QuerySwdbResult { #[inline] pub fn name(&self) -> Result<&str, Option> { self.name_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn name_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()).name.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn installed_version(&self) -> Result<&str, Option> { self.installed_version_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn installed_version_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()) .iversion .as_ref() .map(|s| CStr::from_ptr(s)) } } #[inline] pub fn latest_version(&self) -> Result<&str, Option> { self.latest_version_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn latest_version_raw(&self) -> Option<&CStr> { unsafe { (*self.as_raw()).version.as_ref().map(|s| CStr::from_ptr(s)) } } #[inline] pub fn creation_time(&self) -> Option { let timestamp = unsafe { (*self.as_raw()).created }; if timestamp > 0 { Some(UNIX_EPOCH + Duration::from_secs(timestamp.into())) } else { None } } #[inline] pub fn retrieval_time(&self) -> Option { let timestamp = unsafe { (*self.as_raw()).retrieved }; if timestamp > 0 { Some(UNIX_EPOCH + Duration::from_secs(timestamp.into())) } else { None } } #[inline] pub fn release_time(&self) -> Option { let timestamp = unsafe { (*self.as_raw()).reldate }; if timestamp > 0 { Some(UNIX_EPOCH + Duration::from_secs(timestamp.into())) } else { None } } #[inline] pub fn has_warning(&self) -> bool { unsafe { (*self.as_raw()).warning() } } #[inline] pub fn has_update(&self) -> bool { unsafe { (*self.as_raw()).update() } } #[inline] pub fn is_urgent(&self) -> bool { unsafe { (*self.as_raw()).urgent() } } #[inline] pub fn has_noinfo(&self) -> bool { unsafe { (*self.as_raw()).noinfo() } } #[inline] pub fn is_unknown(&self) -> bool { unsafe { (*self.as_raw()).unknown() } } #[inline] pub fn is_too_old(&self) -> bool { unsafe { (*self.as_raw()).tooold() } } #[inline] pub fn has_error(&self) -> bool { unsafe { (*self.as_raw()).error() } } } impl fmt::Debug for QuerySwdbResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("QuerySwdbResult") .field("raw", &self.as_raw()) .field("name", &self.name_raw()) .field("installed_version", &self.installed_version_raw()) .field("latest_version", &self.latest_version_raw()) .field("creation_time", &self.creation_time()) .field("retrieval_time", &self.retrieval_time()) .field("release_time", &self.release_time()) .field("has_warning", &self.has_warning()) .field("has_update", &self.has_update()) .field("is_urgent", &self.is_urgent()) .field("has_noinfo", &self.has_noinfo()) .field("is_unknown", &self.is_unknown()) .field("is_too_old", &self.is_too_old()) .field("has_error", &self.has_error()) .finish() } } gpgme-0.11.0/src/tofu.rs000064400000000000000000000070221046102023000131150ustar 00000000000000use std::{ ffi::CStr, fmt, marker::PhantomData, str::Utf8Error, time::{Duration, SystemTime, UNIX_EPOCH}, }; use ffi; use crate::NonNull; ffi_enum_wrapper! { /// Upstream documentation: /// [`gpgme_tofu_policy_t`](https://www.gnupg.org/documentation/manuals/gpgme/Changing-TOFU-Data.html#index-gpgme_005ftofu_005fpolicy_005ft) #[non_exhaustive] pub enum TofuPolicy: ffi::gpgme_tofu_policy_t { None = ffi::GPGME_TOFU_POLICY_NONE, Auto = ffi::GPGME_TOFU_POLICY_AUTO, Good = ffi::GPGME_TOFU_POLICY_GOOD, Unknown = ffi::GPGME_TOFU_POLICY_UNKNOWN, Bad = ffi::GPGME_TOFU_POLICY_BAD, Ask = ffi::GPGME_TOFU_POLICY_ASK, } } /// Upstream documentation: /// [`gpgme_tofu_info_t`](https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#index-gpgme_005ftofu_005finfo_005ft) #[derive(Copy, Clone)] pub struct TofuInfo<'a>(NonNull, PhantomData<&'a ()>); unsafe impl Send for TofuInfo<'_> {} unsafe impl Sync for TofuInfo<'_> {} impl<'a> TofuInfo<'a> { impl_wrapper!(ffi::gpgme_tofu_info_t, PhantomData); #[inline] pub fn validity(&self) -> u32 { unsafe { (*self.as_raw()).validity() } } #[inline] pub fn policy(&self) -> TofuPolicy { unsafe { TofuPolicy::from_raw((*self.as_raw()).policy()) } } #[inline] pub fn signature_count(&self) -> u64 { unsafe { (*self.as_raw()).signcount.into() } } #[inline] pub fn encrypted_count(&self) -> u64 { unsafe { (*self.as_raw()).encrcount.into() } } // TODO: Unwrap return value for next major release #[inline] pub fn first_signed(&self) -> Option { let sign_first = unsafe { (*self.as_raw()).signfirst }; Some(UNIX_EPOCH + Duration::from_secs(sign_first.into())) } #[inline] pub fn last_signed(&self) -> Option { let sign_last = unsafe { (*self.as_raw()).signlast }; Some(UNIX_EPOCH + Duration::from_secs(sign_last.into())) } #[inline] pub fn first_encrypted(&self) -> Option { let encr_first = unsafe { (*self.as_raw()).encrfirst }; Some(UNIX_EPOCH + Duration::from_secs(encr_first.into())) } #[inline] pub fn last_encrypted(&self) -> Option { let encr_last = unsafe { (*self.as_raw()).encrlast }; Some(UNIX_EPOCH + Duration::from_secs(encr_last.into())) } #[inline] pub fn description(&self) -> Result<&'a str, Option> { self.description_raw() .map_or(Err(None), |s| s.to_str().map_err(Some)) } #[inline] pub fn description_raw(&self) -> Option<&'a CStr> { unsafe { (*self.as_raw()) .description .as_ref() .map(|s| CStr::from_ptr(s)) } } } impl fmt::Debug for TofuInfo<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TofuInfo") .field("raw", &self.as_raw()) .field("description", &self.description_raw()) .field("validity", &self.validity()) .field("policy", &self.policy()) .field("signature_count", &self.signature_count()) .field("first_signed", &self.first_signed()) .field("last_signed", &self.last_signed()) .field("encrypted_count", &self.encrypted_count()) .field("first_encrypt", &self.first_encrypted()) .field("last_encrypt", &self.last_encrypted()) .finish() } } gpgme-0.11.0/src/utils.rs000064400000000000000000000113151046102023000133000ustar 00000000000000use std::io::{self, prelude::*}; use ffi; use libc; use crate::Error; pub use cstr_argument::CStrArgument; pub type SmallVec = ::smallvec::SmallVec<[T; 4]>; macro_rules! impl_wrapper { ($T:ty$(, $Args:expr)*) => { #[inline] pub unsafe fn from_raw(raw: $T) -> Self { Self(NonNull::<$T>::new(raw).unwrap()$(, $Args)*) } #[inline] pub fn as_raw(&self) -> $T { self.0.as_ptr() } #[inline] pub fn into_raw(self) -> $T { let raw = self.as_raw(); ::std::mem::forget(self); raw } }; } macro_rules! impl_list_iterator { ($Vis:vis struct $Name:ident($Item:ident: $Raw:ty)) => { #[derive(Clone)] $Vis struct $Name<'a>(Option<$Item<'a>>); impl $Name<'_> { #[inline] pub unsafe fn from_list(first: $Raw) -> Self { $Name(first.as_mut().map(|r| $Item::from_raw(r))) } } impl<'a> Iterator for $Name<'a> { type Item = $Item<'a>; #[inline] fn next(&mut self) -> Option { unsafe { self.0.take().map(|c| { self.0 = (*c.as_raw()).next.as_mut().map(|r| $Item::from_raw(r)); c }) } } } impl ::std::iter::FusedIterator for $Name<'_> {} impl ::std::fmt::Debug for $Name<'_> { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { f.debug_list().entries(self.clone()).finish() } } }; } macro_rules! ffi_enum_wrapper { ($(#[$Attr:meta])* $Vis:vis enum $Name:ident($Default:ident): $T:ty { $($(#[$ItemAttr:meta])* $Item:ident = $Value:expr),+ $(,)? }) => { $(#[$Attr])* #[derive(Copy, Clone, Eq, PartialEq, Hash)] $Vis enum $Name { $($(#[$ItemAttr])* $Item,)+ } impl $Name { #[inline] pub unsafe fn from_raw(raw: $T) -> $Name { $(if raw == $Value { $Name::$Item } else )+ { $Name::$Default } } #[inline] pub fn raw(&self) -> $T { match *self { $($Name::$Item => $Value,)+ } } } impl ::std::fmt::Debug for $Name { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { match *self { $($Name::$Item => { write!(f, concat!(stringify!($Name), "::", stringify!($Item), "({:?})"), self.raw()) })+ } } } }; ($(#[$Attr:meta])* $Vis:vis enum $Name:ident: $T:ty { $($(#[$ItemAttr:meta])* $Item:ident = $Value:expr),+ $(,)? }) => { $(#[$Attr])* #[derive(Copy, Clone, Eq, PartialEq, Hash)] $Vis enum $Name { $($(#[$ItemAttr])* $Item,)+ Other($T), } impl $Name { #[inline] pub unsafe fn from_raw(raw: $T) -> $Name { $(if raw == $Value { $Name::$Item } else )+ { $Name::Other(raw) } } #[inline] pub fn raw(&self) -> $T { match *self { $($Name::$Item => $Value,)+ $Name::Other(other) => other, } } } impl ::std::fmt::Debug for $Name { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { match *self { $($Name::$Item => { write!(f, concat!(stringify!($Name), "::", stringify!($Item), "({:?})"), self.raw()) })+ _ => write!(f, concat!(stringify!($Name), "({:?})"), self.raw()), } } } }; } pub(crate) struct FdWriter(libc::c_int); impl FdWriter { pub unsafe fn new(fd: libc::c_int) -> FdWriter { Self(fd) } } impl Write for FdWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let result = unsafe { ffi::gpgme_io_write(self.0, buf.as_ptr().cast(), buf.len()) }; usize::try_from(result).map_err(|_| Error::last_os_error().into()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } pub(crate) trait Ptr { type Inner; } impl Ptr for *mut T { type Inner = T; } impl Ptr for *const T { type Inner = T; } gpgme-0.11.0/tests/common/data/pubdemo.asc000064400000000000000000001077651046102023000165100ustar 0000000000000026 demo keys [2005-10-18] pub 1024D/68697734 1999-03-08 Alpha Test (demo key) uid Alice (demo key) uid Alfa Test (demo key) sub 1024g/46A871F8 1999-03-08 pub 1024D/A9E3B0B2 1999-03-08 Bravo Test (demo key) uid Bob (demo key) sub 1024g/E29BA37F 1999-03-08 pub 1024D/1AFDAB6C 1999-03-08 Charlie Test (demo key) sub 1024g/BC43DA60 1999-03-08 pub 1024D/EB9DC9E6 1999-03-08 Delta Test (demo key) sub 1024g/B0C45424 1999-03-08 pub 1024D/FAEF6D1B 1999-03-08 Echo Test (demo key) uid Eve (demo key) uid Echelon (demo key) sub 1024g/7272144D 1999-03-08 pub 1024D/7372E243 1999-03-08 Foxtrot Test (demo key) sub 1024g/EE45198E 1999-03-08 pub 1024D/8FC282E6 1999-03-08 Golf Test (demo key) sub 1024g/9DCAD354 1999-03-08 pub 1024D/34C6E3F1 1999-03-08 Hotel Test (demo key) sub 1024g/D622AD0A 1999-03-08 pub 1024D/04259677 1999-03-08 India Test (demo key) sub 1024g/61F76C73 1999-03-08 pub 1024D/D2699313 1999-03-08 Juliet Test (demo key) sub 1024g/35F8F136 1999-03-08 pub 1024D/43C2D0C7 1999-03-08 Kilo Test (demo key) sub 1024g/9AF64D02 1999-03-08 pub 1024D/B79103F8 1999-03-08 Lima Test (demo key) sub 1024g/FE56350C 1999-03-08 pub 1024D/BE5CF886 1999-03-08 Mike Test (demo key) uid Mallory (demo key) sub 1024g/4F31EAE8 1999-03-08 pub 1024D/30CEC684 1999-03-08 November Test (demo key) sub 1024g/8B70E472 1999-03-08 pub 1024D/6D9732AC 1999-03-08 Oscar Test (demo key) sub 1024g/2681619F 1999-03-08 pub 1024D/3FF13206 1999-03-08 Papa test (demo key) sub 1024g/63330D9C 1999-03-08 pub 1024D/3C661C84 1999-03-08 Quebec Test (demo key) sub 1024g/A029ACF4 1999-03-08 pub 1024D/777FBED3 1999-03-08 Romeo Test (demo key) sub 1024g/11D102EA 1999-03-08 pub 1024D/A3AE3EA1 1999-03-08 Sierra Test (demo key) sub 1024g/0F1B50B4 1999-03-08 pub 1024D/85A81F38 1999-03-08 Tango Test (demo key) sub 1024g/101C0402 1999-03-08 pub 1024D/653244D6 1999-03-08 Uniform Test (demo key) sub 1024g/5522BDB9 1999-03-08 pub 1024D/61F04784 1999-03-08 Victor Test (demo key) sub 1024g/07287134 1999-03-08 pub 1024D/EC67DBDE 1999-03-08 Whisky Test (demo key) sub 1024g/FD6E27F6 1999-03-08 sub 1024D/E51987C9 2005-10-18 [expires: 2005-10-18] sub 1024R/40DB9D43 2005-10-18 [expires: 2005-10-18] pub 1024D/567FB34A 1999-03-08 XRay Test (demo key) sub 1024g/41E408BE 1999-03-08 pub 1024D/4B11B25F 1999-03-08 Yankee Test (demo key) sub 1024g/F7B080AD 1999-03-08 pub 1024D/54ACD246 1999-03-08 Zulu Test (demo key) sub 1024g/A172C881 1999-03-08 -----BEGIN PGP PUBLIC KEY BLOCK----- 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 fMeR =oe6c -----END PGP PUBLIC KEY BLOCK----- gpgme-0.11.0/tests/common/data/secdemo.asc000064400000000000000000000073031046102023000164570ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v2.1.0-gitb3c71eb (GNU/Linux) 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 U2ofR3zHkQ== =c9V4 -----END PGP PRIVATE KEY BLOCK----- gpgme-0.11.0/tests/common/mod.rs000064400000000000000000000107151046102023000145650ustar 00000000000000#![allow(dead_code)] use std::{ env, fs, io::prelude::*, path::Path, process::{Command, Stdio}, sync::{ atomic::{AtomicUsize, Ordering}, RwLock, }, }; use gpgme::{self, Context, PassphraseRequest, PinentryMode}; use tempfile::TempDir; macro_rules! count { () => {0usize}; ($_head:tt $($tail:tt)*) => {1usize + count!($($tail)*)}; } macro_rules! test_case { (@impl $name:ident($tester:ident) $body:block) => { #[test] fn $name() { let $tester = TEST_CASE.new_test(); $body } }; (@impl #[requires($version:tt)] $name:ident($tester:ident) $body:block) => { test_case!(@impl $name($tester) { #[cfg(feature = $version)] { $body } }); }; ($($(#[requires($version:tt)])? $name:ident($tester:ident) $body:block)+) => { static TEST_CASE: ::once_cell::sync::Lazy<$crate::common::TestCase> = ::once_cell::sync::Lazy::new(|| $crate::common::TestCase::new(count!($($name)+))); $(test_case!(@impl $(#[requires($version)])* $name($tester) $body);)+ }; } pub fn passphrase_cb(_req: PassphraseRequest<'_>, out: &mut dyn Write) -> gpgme::Result<()> { out.write_all(b"abc")?; Ok(()) } fn import_key(key: &[u8]) { let gpg = env::var_os("GPG").unwrap_or("gpg".into()); let mut child = Command::new(&gpg) .arg("--no-permission-warning") .arg("--passphrase") .arg("abc") .arg("--import") .stdin(Stdio::piped()) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn() .unwrap(); child.stdin.as_mut().unwrap().write_all(key).unwrap(); assert!(child.wait().unwrap().success()); } fn setup_agent(dir: &Path) { env::set_var("GNUPGHOME", dir); env::set_var("GPG_AGENT_INFO", ""); let pinentry = Path::new(env!("CARGO_BIN_EXE_pinentry")); if !pinentry.exists() { panic!("Unable to find pinentry program"); } let conf = dir.join("gpg.conf"); fs::write( conf, "ignore-invalid-option allow-weak-key-signatures\n\ allow-weak-key-signatures\n", ) .unwrap(); let agent_conf = dir.join("gpg-agent.conf"); fs::write( agent_conf, format!( "ignore-invalid-option allow-loopback-pinentry\n\ allow-loopback-pinentry\n\ ignore-invalid-option pinentry-mode\n\ pinentry-mode loopback\n\ pinentry-program {}\n", pinentry.to_str().unwrap() ), ) .unwrap(); } pub struct TestCase { count: AtomicUsize, homedir: RwLock>, } impl TestCase { pub fn new(count: usize) -> TestCase { let dir = TempDir::new().unwrap(); setup_agent(dir.path()); import_key(include_bytes!("./data/pubdemo.asc")); import_key(include_bytes!("./data/secdemo.asc")); TestCase { count: AtomicUsize::new(count), homedir: RwLock::new(Some(dir)), } } pub fn new_test(&self) -> Test<'_> { Test { parent: self } } pub fn kill_agent(&self) { let socket = { let homedir = self.homedir.read().unwrap(); homedir.as_ref().unwrap().path().join("S.gpg-agent") }; let mut child = match Command::new("gpg-connect-agent") .arg("-S") .arg(socket) .stdin(Stdio::piped()) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn() { Ok(child) => child, Err(err) => { println!("Unable to kill agent: {}", err); return; } }; if let Some(ref mut stdin) = child.stdin { let _ = stdin.write_all(b"KILLAGENT\nBYE\n"); let _ = stdin.flush(); } if let Err(err) = child.wait() { println!("Unable to kill agent: {}", err); } } fn drop(&self) { if self.count.fetch_sub(1, Ordering::SeqCst) == 1 { self.kill_agent(); self.homedir.write().unwrap().take(); } } } pub struct Test<'a> { parent: &'a TestCase, } impl Drop for Test<'_> { fn drop(&mut self) { self.parent.drop(); } } impl Test<'_> { pub fn create_context(&self) -> Context { let mut ctx = Context::from_protocol(gpgme::Protocol::OpenPgp).unwrap(); let _ = ctx.set_pinentry_mode(PinentryMode::Loopback); ctx } } gpgme-0.11.0/tests/context.rs000064400000000000000000000010731046102023000141770ustar 00000000000000use gpgme::{Context, Error, PinentryMode}; #[macro_use] mod common; test_case! { test_pinentry_mode(test) { let mode = PinentryMode::Loopback; let mut ctx = test.create_context(); match ctx.set_pinentry_mode(mode) { Ok(()) => { // NOTE: UFCS form used here as regression test for // issue #17. assert_eq!(mode, Context::pinentry_mode(&ctx)); } Err(e) if e.code() == Error::NOT_SUPPORTED.code() => (), e @ Err(_) => e.unwrap(), } } } gpgme-0.11.0/tests/create_key.rs000064400000000000000000000056071046102023000146350ustar 00000000000000use std::time::{Duration, SystemTime}; use gpgme::CreateKeyFlags; use self::common::passphrase_cb; #[macro_use] mod common; test_case! { test_create_key(test) { let mut ctx = test.create_context().set_passphrase_provider(passphrase_cb); if !ctx.engine_info().check_version("2.1.13") { return; } let expiration = Duration::from_secs(3600); let res = ctx.create_key_with_flags("test user ", "future-default", expiration, CreateKeyFlags::CERT).unwrap(); let creation = SystemTime::now(); let fpr = res.fingerprint_raw().unwrap(); assert!(res.has_primary_key()); let mut key = ctx.get_key(fpr).unwrap(); assert!(!key.is_bad()); assert!(key.can_certify()); assert!(!key.can_sign()); assert!(!key.can_encrypt()); assert!(!key.can_authenticate()); let primary = key.primary_key().unwrap(); assert!(!primary.is_bad()); assert!(primary.can_certify()); assert!(!primary.can_sign()); assert!(!primary.can_encrypt()); assert!(!primary.can_authenticate()); assert!(primary.expiration_time().unwrap().duration_since(creation).unwrap() <= expiration); assert_eq!(key.subkeys().count(), 1); assert_eq!(key.user_ids().count(), 1); let uid = key.user_ids().nth(0).unwrap(); assert!(!uid.is_bad()); assert_eq!(uid.name(), Ok("test user")); assert_eq!(uid.email(), Ok("test@example.com")); assert_eq!(uid.id(), Ok("test user ")); let res = ctx.create_subkey_with_flags(&key, "future-default", expiration, CreateKeyFlags::AUTH).unwrap(); let creation = SystemTime::now(); assert!(res.has_sub_key()); key.update().unwrap(); assert!(key.can_authenticate()); assert_eq!(key.subkeys().count(), 2); let sub = key.subkeys().find(|k| k.fingerprint_raw() == res.fingerprint_raw()).unwrap(); assert!(!sub.is_bad()); assert!(!sub.can_certify()); assert!(!sub.can_sign()); assert!(!sub.can_encrypt()); assert!(sub.can_authenticate()); assert!(sub.expiration_time().unwrap().duration_since(creation).unwrap() <= expiration); let res = ctx.create_subkey_with_flags(&key, "future-default", expiration, CreateKeyFlags::ENCR | CreateKeyFlags::NOEXPIRE).unwrap(); assert!(res.has_sub_key()); key.update().unwrap(); assert!(key.can_authenticate()); assert_eq!(key.subkeys().count(), 3); let sub = key.subkeys().find(|k| k.fingerprint_raw() == res.fingerprint_raw()).unwrap(); assert!(!sub.is_bad()); assert!(!sub.can_certify()); assert!(!sub.can_sign()); assert!(sub.can_encrypt()); assert!(!sub.can_authenticate()); assert_eq!(sub.expiration_time(), None); } } gpgme-0.11.0/tests/edit.rs000064400000000000000000000050251046102023000134410ustar 00000000000000#![allow(deprecated)] use gpgme; use std::io::prelude::*; use gpgme::{ edit::{self, EditInteractionStatus, Editor}, Error, Result, }; use self::common::passphrase_cb; #[macro_use] mod common; #[derive(Debug, Copy, Clone, Eq, PartialEq)] enum TestEditorState { Start, Fingerprint, Expire, Valid, Uid, Primary, Quit, Save, } impl Default for TestEditorState { fn default() -> Self { TestEditorState::Start } } struct TestEditor; impl Editor for TestEditor { type State = TestEditorState; fn next_state( state: Result, status: EditInteractionStatus<'_>, need_response: bool, ) -> Result { use self::TestEditorState as State; println!("[-- Code: {:?}, {:?} --]", status.code, status.args()); if !need_response { return state; } if status.args() == Ok(edit::PROMPT) { match state { Ok(State::Start) => Ok(State::Fingerprint), Ok(State::Fingerprint) => Ok(State::Expire), Ok(State::Valid) => Ok(State::Uid), Ok(State::Uid) => Ok(State::Primary), Ok(State::Quit) => state, Ok(State::Primary) | Err(_) => Ok(State::Quit), _ => Err(Error::GENERAL), } } else if (status.args() == Ok(edit::KEY_VALID)) && (state == Ok(State::Expire)) { Ok(State::Valid) } else if (status.args() == Ok(edit::CONFIRM_SAVE)) && (state == Ok(State::Quit)) { Ok(State::Save) } else { state.and(Err(Error::GENERAL)) } } fn action(&self, state: Self::State, out: &mut dyn Write) -> Result<()> { use self::TestEditorState as State; match state { State::Fingerprint => out.write_all(b"fpr")?, State::Expire => out.write_all(b"expire")?, State::Valid => out.write_all(b"0")?, State::Uid => out.write_all(b"1")?, State::Primary => out.write_all(b"primary")?, State::Quit => write!(out, "{}", edit::QUIT)?, State::Save => write!(out, "{}", edit::YES)?, _ => return Err(Error::GENERAL), } Ok(()) } } test_case! { test_edit(test) { test.create_context().with_passphrase_provider(passphrase_cb, |ctx| { let key = ctx.find_keys(Some("Alpha")).unwrap().next().unwrap().unwrap(); ctx.edit_key_with(&key, TestEditor, &mut Vec::new()).unwrap(); }); } } gpgme-0.11.0/tests/encrypt_simple.rs000064400000000000000000000020451046102023000155500ustar 00000000000000use gpgme; use self::common::passphrase_cb; #[macro_use] mod common; test_case! { test_simple_encrypt_decrypt(test) { let mut ctx = test.create_context(); let key = ctx.find_keys(Some("alfa@example.net")).unwrap().nth(0).unwrap().unwrap(); ctx.set_armor(true); ctx.set_text_mode(true); let mut ciphertext = Vec::new(); ctx.encrypt_with_flags(Some(&key), "Hello World", &mut ciphertext, gpgme::EncryptFlags::ALWAYS_TRUST).unwrap(); assert!(ciphertext.starts_with(b"-----BEGIN PGP MESSAGE-----")); drop(ctx); let mut plaintext = Vec::new(); test.create_context().with_passphrase_provider(passphrase_cb, |ctx| { ctx.decrypt(&ciphertext, &mut plaintext).unwrap(); }); assert_eq!(plaintext, b"Hello World"); let mut plaintext = Vec::new(); let mut ctx = test.create_context().set_passphrase_provider(passphrase_cb); ctx.decrypt(&ciphertext, &mut plaintext).unwrap(); assert_eq!(plaintext, b"Hello World"); } } gpgme-0.11.0/tests/encrypt_symmetric.rs000064400000000000000000000016631046102023000163000ustar 00000000000000use self::common::passphrase_cb; #[macro_use] mod common; test_case! { test_symmetric_encrypt_decrypt(test) { let mut ciphertext = Vec::new(); test.create_context().with_passphrase_provider(passphrase_cb, |ctx| { ctx.set_armor(true); ctx.set_text_mode(true); ctx.encrypt_symmetric("Hello World", &mut ciphertext).unwrap(); }); assert!(ciphertext.starts_with(b"-----BEGIN PGP MESSAGE-----")); let mut plaintext = Vec::new(); test.create_context().with_passphrase_provider(passphrase_cb, |ctx| { ctx.decrypt(&ciphertext, &mut plaintext).unwrap(); }); assert_eq!(plaintext, b"Hello World"); let mut plaintext = Vec::new(); let mut ctx = test.create_context().set_passphrase_provider(passphrase_cb); ctx.decrypt(&ciphertext, &mut plaintext).unwrap(); assert_eq!(plaintext, b"Hello World"); } } gpgme-0.11.0/tests/export.rs000064400000000000000000000022571046102023000140410ustar 00000000000000use gpgme::{CreateKeyFlags, ExportMode}; use self::common::passphrase_cb; #[macro_use] mod common; test_case! { test_export(test) { let mut ctx = test.create_context().set_passphrase_provider(passphrase_cb); ctx.set_offline(true); ctx.set_armor(true); let key = ctx.find_keys(Some("alfa@example.net")).unwrap().nth(0).unwrap().unwrap(); let mut data = Vec::new(); ctx.export(key.fingerprint_raw(), ExportMode::empty(), &mut data).unwrap(); assert!(!data.is_empty()); } test_export_secret(test) { let mut ctx = test.create_context(); ctx.set_offline(true); ctx.set_armor(true); let res = match ctx.create_key_with_flags("test user ", "future-default", Default::default(), CreateKeyFlags::NOPASSWD) { Ok(r) => r, Err(e) if e.code() == gpgme::Error::NOT_SUPPORTED.code() => return, Err(e) => panic!("error: {:?}", e), }; let fpr = res.fingerprint_raw().unwrap(); let mut data = Vec::new(); ctx.export(Some(fpr), ExportMode::SECRET, &mut data).unwrap(); assert!(!data.is_empty()); } } gpgme-0.11.0/tests/keylist.rs000064400000000000000000000012511046102023000141750ustar 00000000000000use gpgme; #[macro_use] mod common; test_case! { test_key_list(test) { let mut ctx = test.create_context(); let keys: Vec<_> = ctx.find_keys(Some("alfa@example.net")).unwrap() .collect::>().unwrap(); assert_eq!(keys.len(), 1, "incorrect number of keys"); let key = &keys[0]; assert_eq!(key.id(), Ok("2D727CC768697734")); assert_eq!(key.subkeys().count(), 2); let subkeys: Vec<_> = key.subkeys().collect(); assert_eq!(subkeys[0].algorithm(), gpgme::KeyAlgorithm::Dsa); assert_eq!(subkeys[1].algorithm(), gpgme::KeyAlgorithm::ElgamalEncrypt); } } gpgme-0.11.0/tests/keysign.rs000064400000000000000000000017761046102023000141760ustar 00000000000000use gpgme::KeyListMode; use self::common::passphrase_cb; #[macro_use] mod common; test_case! { test_sign_key(test) { let mut ctx = test.create_context(); if !ctx.engine_info().check_version("2.1.12") { return; } ctx.add_key_list_mode(KeyListMode::SIGS).unwrap(); let signer = ctx.find_secret_keys(Some("alfa@example.net")).unwrap().nth(0).unwrap().unwrap(); ctx.add_signer(&signer).unwrap(); let mut key = ctx.find_keys(Some("bravo@example.net")).unwrap().nth(0).unwrap().unwrap(); assert!(!key.user_ids().nth(0).unwrap().signatures().any(|s| { signer.id_raw() == s.signer_key_id_raw() })); ctx.with_passphrase_provider(passphrase_cb, |ctx| { ctx.sign_key(&key, None::, Default::default()).unwrap(); }); key.update().unwrap(); assert!(key.user_ids().nth(0).unwrap().signatures().any(|s| { signer.id_raw() == s.signer_key_id_raw() })); } } gpgme-0.11.0/tests/verify.rs000064400000000000000000000020541046102023000140170ustar 00000000000000#[macro_use] mod common; const TEST_MSG1: &'static [u8] = b"-----BEGIN PGP MESSAGE-----\n\ \n\ owGbwMvMwCSoW1RzPCOz3IRxjXQSR0lqcYleSUWJTZOvjVdpcYmCu1+oQmaJIleH\n\ GwuDIBMDGysTSIqBi1MApi+nlGGuwDeHao53HBr+FoVGP3xX+kvuu9fCMJvl6IOf\n\ y1kvP4y+8D5a11ang0udywsA\n\ =Crq6\n\ -----END PGP MESSAGE-----\n"; test_case! { test_signature_key(test) { let mut output = Vec::new(); let mut ctx = test.create_context(); let result = ctx.verify_opaque(TEST_MSG1, &mut output).unwrap(); assert_eq!(result.signatures().count(), 1); let sig = result.signatures().nth(0).unwrap(); let key = ctx.get_key(sig.fingerprint_raw().unwrap()).unwrap(); for subkey in key.subkeys() { if subkey.fingerprint_raw() == sig.fingerprint_raw() { return; } } panic!(); } }