diff --git a/INSTALL b/INSTALL
deleted file mode 100755
index 1dc018c..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/bash
-
-echo -e "\nWill install Pyrite application files to /usr/share/pyrite/"
-echo "Enter to accept; otherwise enter a different dir, e.g., /home/$USER/.local/share/pyrite/"
-read -p "[/usr/share/pyrite]: " install_dir
-[[ -z $install_dir ]] && install_dir=/usr/share/pyrite || install_dir=${install_dir%/}
-if [[ -d $install_dir ]]; then
- echo "Directory '$install_dir' already exists."
- echo "All files in directory will be deleted prior to copying new pyrite files."
- read -p "Continue? [y/n] "
- [[ $REPLY != y ]] && exit
-else
- if ! mkdir -vp "$install_dir"; then
- echo "Could not create directory '$install_dir'"
- if [[ ! -w ${install_dir%/*} ]]; then
- echo "Need write permission to mkdir in '${install_dir%/*}'"
- echo "Re-run this script as root, i.e., 'sudo $0' or 'su -c $0'"
- fi
- exit 1
- fi
-fi
-echo OK
-
-
-echo -e "\nWill install a shortcut ('pyrite') to /usr/bin/"
-echo "Press Enter to accept; otherwise enter a different dir, e.g., /home/$USER/bin/"
-read -p "[/usr/bin/]: " bin_dir
-[[ -z $bin_dir ]] && bin_dir=/usr/bin
-if [[ ! -w $bin_dir ]]; then
- echo "Need write permission to create shortcut in '$bin_dir'"
- echo "Re-run this script as root, i.e., 'sudo $0' or 'su -c $0'"
- exit 2
-fi
-echo OK
-
-
-echo -e "\nWill install a desktop shortcut ('pyrite.desktop') to /usr/share/applications/"
-echo "Press Enter to accept; otherwise enter a different dir, e.g., /home/$USER/.local/share/applications/"
-read -p "[/usr/share/applications/]: " shortcut_dir
-[[ -z $shortcut_dir ]] && shortcut_dir=/usr/share/applications
-if [[ ! -w $shortcut_dir ]]; then
- echo "Need write permission to create desktop shortcut in '$shortcut_dir'"
- echo "Re-run this script as root, i.e., 'sudo $0' or 'su -c $0'"
- exit 3
-fi
-echo OK
-
-
-echo -e "\nReady to copy files. Last chance to cancel with Ctrl-c. Enter to continue."
-read -p ": "
-
-# Remove contents of install dir (usually /usr/share/pyrite/)
-rm -r "$install_dir"/* 2>/dev/null
-
-# Copy files to install dir
-cp -rv --preserve=mode,timestamps modules ui pyrite.py "$install_dir"
-
-# Modify assetdir variable in cfg.py to point to the proper install dir
-sed -i "/^ASSETDIR/s,'.*','$install_dir/'," "$install_dir/modules/cfg.py"
-
-# Copy desktop shortcut
-cp -v --preserve=mode,timestamps pyrite.desktop "$shortcut_dir"
-
-# Setup cmdline shortcut
-echo -e "\nCreating shortcut $bin_dir/pyrite: "
-cat >$bin_dir/pyrite <
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d1a451c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+install:
+ DESTDIR=$(DESTDIR) ./INSTALL.sh
+uninsall:
+ rm -rf "$(DESTDIR)/usr/share/pyrite/ui"
+ rm -rf "$(DESTDIR)/usr/lib/python3/dist-packages/pyrite"
+ rm -f "$(DESTDIR)/usr/share/applications/pyrite.desktop"
+ rm -f "$(DESTDIR)/usr/share/icons/hicolor/scalable/apps/pyrite.svg"
+ rm -f "$(DESTDIR)/usr/bin/pyrite"
diff --git a/README.md b/README.md
index f401e6b..d86c109 100644
--- a/README.md
+++ b/README.md
@@ -3,52 +3,68 @@ Pyrite - Python/GTK+ encryption/signing frontend for GnuPG and OpenSSL
![](http://b19.org/linux/pyrite/1enc_txt.png)
+## FEATURES
-FEDORA/RHEL7 INSTALLATION
--------------------------
-There's an RPM (and yum repository) @ [people.redhat.com/rsawhill/rpms](http://people.redhat.com/rsawhill/rpms/). To configure it and install Pyrite, simply run the following as root:
+Pyrite acts as a frontend for GnuPG, doing symmetric or asymmetric encrypting/decrypting, as well as signing and verifying.
+Additionally, it can use OpenSSL for simple symmetric encryption/decryption.
+
+Pyrite can operate on text input or can take input and output filenames (text or binary) to pass directly to the backend program (i.e., gpg or openssl).
+
+As you can see from the screenshots, Pyrite can utilize virtually all the encrypting features of GnuPG -- you can mix and match passphrase & public-key encryption & signing with one file, just like gpg, which will require interacting with your gpg-agent.
+Or you can keep it simple and just use a passphrase as a shared key, in which case gpg-agent is bypassed and you only have to type the passphrase once.
+
+Also shown in the screenshots is a Sign/Verify mode, where you can choose between the three types of signing: normal (Pyrite calls it "embedded"), where a signed copy of the message is created; clearsign, where the message is wrapped with a plaintext ASCII sig; or detached-sign, where a separate sig file is created.
+
+If you're operating directly on files (in sign or encrypt mode) instead of ASCII text in the Pyrite window, you can choose what kind of output you want -- ASCII-armored (base64-encoded) text or normal binary output.
+
+Not shown in the screenshots is drag & drop. You can drag text files onto the Message area and they are loaded up and you can drag text or binary files onto the *Input File For Direct Operation* button to set that.
+
+If you end up working on very large input, you'll get a chance to *really* see the progress bar + pause/cancel buttons.
+At the moment the progress bar doesn't report actual progress (that's coming), but the buttons do what they advertise, pausing or canceling the backend processing.
+
+To top it all off, everything is configurable.
+There's a preferences dialog that lets you play with all the settings, from tweaking gpg verbosity to setting the default operating mode to choosing your favorite cipher to configuring font size/color and window opacity.
+
+If you find yourself wondering about a particular feature, just hover your mouse over its widget -- there are detailed tooltips for everything.
+
+
+## FEDORA/RHEL7 INSTALLATION
+There's an RPM (and yum repository) @ [people.redhat.com/rsawhill/rpms](https://people.redhat.com/rsawhill/rpms/). To configure it and install Pyrite, simply run the following as root:
```
-yum install http://people.redhat.com/rsawhill/rpms/latest-rsawaroha-release.rpm
+yum install https://people.redhat.com/rsawhill/rpms/latest-rsawaroha-release.rpm
yum install pyrite
```
Requirements and package names:
-- gtk2 >= v2.24: `gtk2`
-- python2 >= v2.7: `python`
-- pygtk: `pygtk2`
-- gpg/openssl: `gnupg2` or `gnupg` or `openssl`
-
-*As per above, Pyrite is not compatible with RHEL6.*
+- GTK3: `gtk3`
+- Python3: `python3`
+- `python3-gi`, `python3-gi-cairo`
+- PGP `gnupg` or OpenSSL `openssl`
-DEBIAN/UBUNTU/OTHER LINUX INSTALLATION
---------------------------------------
-There is a simple interactive shell installer. Before using it, ensure you have the following on your Linux system (Ubuntu package names):
+## DEBIAN/UBUNTU/OTHER LINUX INSTALLATION
-- gtk2 >= v2.24: `libgtk2.0-bin`
-- python2 >= v2.7: `python`
-- pygtk: `python-gtk2`
-- gpg/openssl: `gnupg2` or (`gnupg` and `gnupg-agent`) or `openssl`
+There is a simple interactive shell installer.
-If requirements are met, clone the Pyrite repo with `git clone git://github.com/ryran/pyrite.git` **OR** [download a zip of the source](https://github.com/ryran/pyrite/archive/master.zip).
+If requirements are met, clone the Pyrite repo with `git clone https://github.com/ryran/pyrite.git` **OR** [download a zip of the source](https://github.com/ryran/pyrite/archive/master.zip).
From the root source folder execute the interactive `INSTALL` script.
-MORE SCREENSHOTS (v1.0.1):
--------------------------------
+## MORE SCREENSHOTS (v1.0.1):
+
![](http://b19.org/linux/pyrite/2clearsign_txt.png)
![](http://b19.org/linux/pyrite/3enc_prog.png)
![](http://b19.org/linux/pyrite/4dec_txt.png)
![](http://b19.org/linux/pyrite/5openssl_txt.png)
![](http://b19.org/linux/pyrite/6prefs.png)
-**`pyrite` command-line options:**
+## `pyrite` command-line options
+Type `pyrite --help`:
```
-[rsaw:~]$ pyrite --help
usage: pyrite [-h] [-d | -t] [-e | -s] [-c] [-r RECIP] [-k KEYUID]
[-b {gpg,openssl}]
[INPUT]
@@ -75,48 +91,16 @@ optional arguments:
```
-FEATURES
-----------
-Pyrite acts as a frontend for GnuPG, doing symmetric or asymmetric encrypting/decrypting, as well as signing and verifying. Additionally, it can use OpenSSL for simple symmetric encryption/decryption.
-
-Pyrite can operate on text input or can take input and output filenames (text or binary) to pass directly to the backend program (i.e., gpg/gpg2 or openssl).
-
-As you can see from the screenshots, Pyrite can utilize virtually all of the encrypting features of GnuPG -- you can mix and match passphrase & public-key encryption & signing with one file, just like gpg, which will require interacting with your gpg-agent. Or you can keep it simple and just use a passphrase as a shared key, in which case gpg-agent is bypassed and you only have to type the passphrase once.
-
-Also shown in the screenshots is a Sign/Verify mode, where you can choose between the three types of signing: normal (Pyrite calls it "embedded"), where a signed copy of the message is created; clearsign, where the message is wrapped with a plaintext ASCII sig; or detached-sign, where a separate sig file is created.
-
-If you're operating directly on files (in sign or encrypt mode) instead of ASCII text in the Pyrite window, you can choose what kind of output you want -- ASCII-armored (base64-encoded) text or normal binary output.
-
-Not shown in the screenshots is drag & drop. You can drag text files onto the Message area and they are loaded up and you can drag text or binary files onto the *Input File For Direct Operation* button to set that.
-
-If you end up working on very large input, you'll get a chance to *really* see the progress bar + pause/cancel buttons. At the moment the progress bar doesn't report actual progress (that's coming), but the buttons do what they advertise, pausing or canceling the backend processing.
-
-To top it all off, everything is configurable. There's a preferences dialog that lets you play with all the settings, from tweaking gpg verbosity to setting the default operating mode to choosing your favorite cipher to configuring font size/color and window opacity.
-
-If you find yourself wondering about a particular feature, just hover your mouse over its widget -- there are detailed tooltips for everything.
-
+## BUGS and TODO
-BUGS
-----------
-1) After launching Pyrite, the **first** drag/drop of a file onto the *Input File For Direct Operation* GtkFileChooserButton fails. After that the button works properly. I've been seeking out expertise on this weird bug but I haven't gotten anywhere. If you have any hints, hit me up, or check out [my post about it on stackoverflow](http://stackoverflow.com/questions/9047844/pygtk-troubles-with-drag-and-drop-file-to-gtkfilechooserbutton).
-
-2) No undo. It wasn't a top priority at the beginning, but I think it's pretty essential for an application that basically contains a text editor to have an undo/redo stack. I'll do it eventually.
-
-
-BACKGROUND
-----------
-
-The original goal of this project was to make symmetric {en,de}cryption more accessible and easy to use. While GPG rocks if you're comfortable on the commandline (for both symmetric & public-key), and there are GUI encryption options for public-key encryption (seahorse-plugins for nautilus being the best, in my opinion), there's not much out there for people who need to do the simplest kind of encryption -- with a shared passphrase.
-
-After creating a few simple apps with BASH scripting, I decided it was time to learn Python. After the first few days I was in love.
-
-Long story short, after a couple weeks of learning, I released my first version of this project in January 2012, quickly added public-key encryption, signing, & verifying, and have been improving it ever since. This being my first learning experience with GTK+, I have lots more to learn, but I'm damn proud of Pyrite.
+- No undo. It wasn't a top priority at the beginning, but I think it's pretty essential for an application that basically contains a text editor to have an undo/redo stack. I'll do it eventually.
+- Icons for encrypt, decrypt, sign, verify buttons, application
+- Update notifications
PLEASE contact me (or [post a new issue on the tracker](/ryran/pyrite/issues)) with any suggestions, feedback, bug reports, or questions!
-AUTHORS
--------
+## AUTHORS
As far as direct contributions go, so far it's just me, [ryran](/ryran), aka rsaw, aka [Ryan Sawhill Aroha](http://b19.org).
@@ -125,8 +109,7 @@ The project could really use a little assistance from an artist -- it doesn't ha
-LICENSE
--------
+## LICENSE
Copyright (C) 2012, 2013 [Ryan Sawhill Aroha](http://b19.org)
@@ -138,19 +121,17 @@ the Free Software Foundation, either version 3 of the License, or
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License @[gnu.org/licenses/gpl.html](http://gnu.org/licenses/gpl.html>) for more details.
-
-
+General Public License @[gnu.org/licenses/gpl.html](https://gnu.org/licenses/gpl.html>) for more details.
--------
-
Hmmmm. You're still here?
-Oh. You must be wondering why the name [*Pyrite*](http://en.wikipedia.org/wiki/Pyrite), eh?
+Oh. You must be wondering why the name [*Pyrite*](https://en.wikipedia.org/wiki/Pyrite), eh?
-Well, I'll let my friend River--who came up with the name--explain it to you:
+Well, I'll let my friend River who came up with the name explain it to you:
-*"It should be 'Pyrite', because people think they are getting your data, but really it's just gibberish to them. Fool's gold."*
+> It should be 'Pyrite', because people think they are getting your data, but really it's just gibberish to them.
+> Fool's gold.
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..52cced5
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+pyrite-crypt (2.0.0-1) stable; urgency=medium
+
+ * update to GTK3 and Python3
+
+ -- Sergey Ponomarev Sun, 29 Oct 2020 00:00:00 +0000
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..b4de394
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+11
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..99cd6d1
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,13 @@
+Source: pyrite-crypt
+Section: utils
+Priority: optional
+Maintainer: Sergey Ponomarev
+Build-Depends: debhelper (>=10)
+Standards-Version: 4.1.4
+Homepage: https://github.com/stokito/pyrite
+
+Package: pyrite-crypt
+Architecture: all
+Depends: python3, python3-gi, python3-gi-cairo, gnupg, openssl
+Description: Encryption/signing application
+ GUI frontend for GnuPG (gpg) encrypting, decrypting, signing, and verifying.
\ No newline at end of file
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..006bdae
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,8 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: pyrite-crypt
+Source: https://github.com/stokito/pyrite
+
+Files: *
+Copyright: 2012 Ryan Sawhill Aroha
+ 2023 Sergey Ponomarev
+License: GPLv3
diff --git a/debian/dirs b/debian/dirs
new file mode 100644
index 0000000..047500f
--- /dev/null
+++ b/debian/dirs
@@ -0,0 +1,5 @@
+usr/bin
+usr/share/pyrite/
+usr/lib/python3/dist-packages/
+usr/share/applications
+usr/share/icons/hicolor/scalable/apps/
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..6d3eb70
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,4 @@
+#!/usr/bin/make -f
+
+%:
+ DESTDIR=$(CURDIR) dh $@
diff --git a/debian/source/format b/debian/source/format
new file mode 100755
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/modules/messages.py b/modules/messages.py
deleted file mode 100644
index c79fa61..0000000
--- a/modules/messages.py
+++ /dev/null
@@ -1,242 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of Pyrite.
-# Last file mod: 2013/09/15
-# Latest version at
-# Copyright 2012, 2013 Ryan Sawhill Aroha
-#
-# License:
-#
-# Pyrite is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Pyrite is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Pyrite. If not, see .
-#
-#------------------------------------------------------------------------------
-
-SUCCESS = 0
-INFO = 1
-QUESTION = 2
-WARNING = 3
-ERROR = 4
-
-def msg(text, type, icon, timeout=5):
- """Dictionar-ify input arguments."""
- return {'text': text, 'type': type, 'icon': icon, 'timeout': timeout}
-
-
-#------------------------------------------------------------------------------
- # INFOBAR MESSAGES FOR MAIN WINDOW
-MESSAGE_DICT = dict(
-
- #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Backend Engine
- engine_openssl_missing = msg(
- ("Shockingly, your system does not appear to have OpenSSL."),
- INFO, WARNING),
-
- engine_gpg_missing = msg(
- ("GnuPG not found. Operating in OpenSSL fallback-mode.\n"
- "To make full use of this program you need either gpg or gpg2 installed.\n"
- "Without one of them, you won't have access key-based functions like\n"
- "asymmetric encryption or singing."),
- INFO, WARNING, 20),
-
- engine_all_missing = msg(
- ("This program requires one of: gpg, gpg2, or openssl\n"
- "None of these were found on your system. You can look around\n"
- "the interface, but to have real fun you'll need to install gpg or gpg2\n"
- "from your linux distribution's software repository."),
- ERROR, WARNING, 0),
-
- engine_openssl_notice = msg(
- ("OpenSSL only supports symmetric {{en,de}}cryption.\n"
- "All key-based functions are disabled."),
- INFO, INFO, 7),
-
- #- - - - - - - - - - - - - - - - - - - - - Textview Message Area Operations
- txtview_empty = msg(
- ("{customtext}"),
- INFO, WARNING, 2),
-
- txtview_fileopen_error = msg(
- ("Error. Could not open file:\n"
- "{filename}"),
- WARNING, ERROR),
-
- txtview_fileopen_binary_error = msg(
- ("To operate on binary files, use the\n"
- "Input File For Direct Operation chooser button."),
- INFO, WARNING, 8),
-
- txtview_save_success = msg(
- ("Saved contents of Message area to file:\n"
- "{filename}"),
- INFO, SUCCESS),
-
- txtview_save_error = msg(
- ("Error. Could not save to file:\n"
- "{filename}"),
- WARNING, ERROR),
-
- txtview_copyall_success = msg(
- ("Copied contents of Message area to clipboard."),
- INFO, SUCCESS, 3),
-
- #- - - - - - - - - - - - - - - - - - - - - - - - - - - Filemode Operations
- filemode_fileopen_error = msg(
- ("Error. Could not open file:\n"
- "{filename}\n"
- "Choose a new file."),
- WARNING, ERROR),
-
- filemode_blue_banner = msg(
- ("Encrypt, Decrypt, Sign, or Verify?\n"
- "Choose an action to perform on file:\n"
- "{filename}\n"
- "You will be prompted for an output filename if necessary."),
- QUESTION, QUESTION, 0),
-
- #- - - - - - - - - - - - - - - Main xface (Enc/Dec/Sign/Verify) Operations
- x_missing_passphrase= msg(
- ("Passphrase?"),
- INFO, QUESTION, 3),
-
- x_canceled_filemode = msg(
- ("{customtext} operation canceled.\n"
- "To choose different input or output filenames, select Cancel\n"
- "from the blue bar below."),
- INFO, WARNING, 6),
-
- x_canceled_textmode = msg(
- ("{customtext} operation canceled."),
- INFO, WARNING, 4),
-
- x_opensslenc_success_filemode = msg(
- ("OpenSSL encrypted input file with {customtext} cipher;\n"
- "saved output to file:\n"
- "{filename}\n"
- "In order to decrypt that file in the future, you will need to \n"
- "remember which cipher you used .. or guess until you figure it out."),
- INFO, SUCCESS, 10),
-
- x_opensslenc_success_textmode = msg(
- ("OpenSSL encrypted input using {customtext} cipher.\n"
- "In order to decrypt the output in the future, you will need to \n"
- "remember which cipher you used .. or guess until you figure it out."),
- INFO, SUCCESS, 9),
-
- x_crypt_success_filemode = msg(
- ("Saved {customtext}rypted copy of input to file:\n"
- "{filename}"),
- INFO, SUCCESS),
-
- x_sign_success_filemode = msg(
- ("Saved signed copy of input to file:\n"
- "{filename}"),
- INFO, SUCCESS),
-
- x_detachsign_success_filemode = msg(
- ("Saved detached signature of input to file:\n"
- "{filename}"),
- INFO, SUCCESS),
-
- x_verify_success = msg(
- ("Signature verified. Data integrity intact."),
- INFO, SUCCESS, 4),
-
- x_verify_failed = msg(
- ("Signature or data integrity could not be verified.\n"
- "See Task Status for details."),
- WARNING, ERROR, 7),
-
- x_missing_recip = msg(
- ("For whom do you want to encrypt your message?\n"
- "If you don't want to enter recipients and you don't want to select\n"
- " Enc. To Self, you must add one of the directives\n"
- "\tdefault-recipient-self\n"
- "\tdefault-recipient name\n"
- "to your gpg.conf file."),
- WARNING, QUESTION, 0),
-
- x_generic_failed_filemode = msg(
- ("Problem {customtext}ing file.\n"
- "See Task Status for details. Try a different passphrase or Cancel."),
- WARNING, ERROR, 8),
-
- x_generic_failed_textmode = msg(
- ("Problem {customtext}ing input.\n"
- "See Task Status for details."),
- WARNING, ERROR),
-
- #- - - - - - - - - - - - - - - - - - - - - - - - - OpenSSL Cipher Warnings
- cipher_openssl_no_default = msg(
- ("OpenSSL has no default cipher.\n"
- "AES256 is a good choice."),
- INFO, INFO, 7),
-
- cipher_openssl_no_twofish = msg(
- ("OpenSSL has no support for the Twofish cipher."),
- INFO, INFO),
-
- cipher_openssl_aes_note = msg(
- ("Note for the command-line geeks:\n"
- "AES translates to OpenSSL's aes-128-cbc."),
- INFO, INFO),
-
- #- - - - - - - - - - - - - - - - - - - - - - - - - - - Preferences Actions
- preferences_save_success = msg(
- ("Saved preferences to {filename}\n"
- "but no changes made to current session."),
- INFO, SUCCESS),
-
- preferences_apply_success = msg(
- ("Saved preferences to {filename}\n"
- "and applied them to current session."),
- INFO, SUCCESS),
-
- )
-
-
-
-#------------------------------------------------------------------------------
- # INFOBAR MESSAGES FOR PREFERENCES DIALOG
-PREFS_MESSAGE_DICT = dict(
-
- prefs_save_failed = msg(
- ("Saving preferences failed.\n"
- "Unable to open config file {filename} for writing."),
- ERROR, WARNING, 10),
-
- prefs_reverted = msg(
- ("Reverted to user-saved preferences."),
- INFO, SUCCESS, 3),
-
- prefs_reset_to_defaults = msg(
- ("Preferences reset to defaults. You still need to Save or Apply."),
- INFO, SUCCESS, 3),
-
- prefs_notice_enctoself = msg(
- ("If you want Encrypt to Self on in Symmetric mode, you must set\n"
- "Encryption Type to 'Both'."),
- INFO, INFO),
-
- prefs_notice_addsig = msg(
- ("If you want Add Signature on in Symmetric mode, you must also enable\n"
- "Advanced."),
- INFO, INFO),
-
- prefs_notice_enc_both = msg(
- ("In order for both encryption types to be on by default, Advanced will also be\n"
- "turned on, whether or not you select it now."),
- INFO, INFO),
-
- )
diff --git a/pyrite.desktop b/pyrite.desktop
index 435173e..6d0e876 100644
--- a/pyrite.desktop
+++ b/pyrite.desktop
@@ -1,10 +1,10 @@
[Desktop Entry]
Name=Pyrite
-Comment=OpenSSL & GnuPG (gpg/gpg2) encryption/signing front-end
+Comment=OpenSSL & GnuPG (gpg) encryption/signing front-end
+Keywords=encryption;security;sign;signature;gpg;pgp;openssl;
Exec=pyrite
Terminal=false
Type=Application
-Icon=/usr/share/pixmaps/password.png
-# NEEEEEEED a real icon
-Categories=GNOME;Security
-
+Icon=/usr/share/icons/hicolor/scalable/apps/pyrite.svg
+Categories=GNOME;GTK;Security;Utility;
+MimeType=application/pgp-encrypted;
\ No newline at end of file
diff --git a/pyrite.py b/pyrite.py
index 8c33798..8e1c8b5 100755
--- a/pyrite.py
+++ b/pyrite.py
@@ -1,9 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of Pyrite.
-# Last file mod: 2013/09/15
-# Latest version at
+#!/usr/bin/env python3
# Copyright 2012, 2013 Ryan Sawhill Aroha
#
# License:
@@ -20,21 +15,11 @@
#
# You should have received a copy of the GNU General Public License
# along with Pyrite. If not, see .
-#
-#------------------------------------------------------------------------------
-#
-# TODO:
-# * Icons for for encrypt, decrypt, sign, verify buttons, application
-# * Undo stack. Blech. Kill me.
-# * Update notifications
-# * BUG: First drag/drop onto FileChooserButton fails; 2nd, 3rd, etc succeed.
-# It's a GTK+ issue. Reported. bugzilla.gnome.org/show_bug.cgi?id=669718
-#
-#------------------------------------------------------------------------------
import argparse
from sys import argv
-import modules.core
+
+import pyrite.core
# Parse command-line arguments
parser = argparse.ArgumentParser(
@@ -77,15 +62,14 @@
args = parser.parse_args()
# If no cmdline options specified, let's save some cycles later
-if len(argv) == 1: args = None
-
+if len(argv) == 1:
+ args = None
if __name__ == "__main__":
-
- FeS2 = modules.core.Pyrite(args)
+
+ p = pyrite.core.Pyrite(args)
try:
- FeS2.main()
+ p.main()
except KeyboardInterrupt:
- print
+ print()
exit()
-
diff --git a/pyrite.svg b/pyrite.svg
new file mode 100644
index 0000000..ede1054
--- /dev/null
+++ b/pyrite.svg
@@ -0,0 +1,36 @@
+
+
diff --git a/modules/__init__.py b/pyrite/__init__.py
similarity index 100%
rename from modules/__init__.py
rename to pyrite/__init__.py
diff --git a/modules/cfg.py b/pyrite/cfg.py
similarity index 50%
rename from modules/cfg.py
rename to pyrite/cfg.py
index 4b9daf7..8973607 100644
--- a/modules/cfg.py
+++ b/pyrite/cfg.py
@@ -1,9 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of Pyrite.
-# Last file mod: 2013/09/15
-# Latest version at
+#!/usr/bin/env python3
# Copyright 2012, 2013 Ryan Sawhill Aroha
#
# License:
@@ -20,28 +15,33 @@
#
# You should have received a copy of the GNU General Public License
# along with Pyrite. If not, see .
-#
-#------------------------------------------------------------------------------
-import gtk
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
from os import getenv
# Important variables
-VERSION = 'v1.0.2'
-ASSETDIR = '/usr/share/pyrite/'
-USERPREF_FILE = getenv('HOME') + '/.pyrite'
-USERPREF_FORMAT_INFO = {'version':'Must6fa'}
+VERSION = 'v2.0.0'
+ASSETDIR = '/usr/share/pyrite/'
+USERPREF_FILE = getenv('HOME') + '/.pyrite'
+USERPREF_FORMAT_INFO = {'version': 'Must6fa'}
# List of possible Infobar message types
-MSGTYPES = [0,
- gtk.MESSAGE_INFO, # 1
- gtk.MESSAGE_QUESTION, # 2
- gtk.MESSAGE_WARNING, # 3
- gtk.MESSAGE_ERROR] # 4
+MSGTYPES = [
+ 0,
+ Gtk.MessageType.INFO, # 1
+ Gtk.MessageType.QUESTION, # 2
+ Gtk.MessageType.WARNING, # 3
+ Gtk.MessageType.ERROR # 4
+]
# List of possible images to show in Infobar
-IMGTYPES = [gtk.STOCK_APPLY, # 0
- gtk.STOCK_DIALOG_INFO, # 1
- gtk.STOCK_DIALOG_QUESTION, # 2
- gtk.STOCK_DIALOG_WARNING, # 3
- gtk.STOCK_DIALOG_ERROR] # 4
+IMGTYPES = [
+ Gtk.STOCK_APPLY, # 0
+ Gtk.STOCK_DIALOG_INFO, # 1
+ Gtk.STOCK_DIALOG_QUESTION, # 2
+ Gtk.STOCK_DIALOG_WARNING, # 3
+ Gtk.STOCK_DIALOG_ERROR # 4
+]
diff --git a/modules/core.py b/pyrite/core.py
similarity index 63%
rename from modules/core.py
rename to pyrite/core.py
index 2d69fa4..2ef1ffb 100644
--- a/modules/core.py
+++ b/pyrite/core.py
@@ -1,9 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of Pyrite.
-# Last file mod: 2013/09/15
-# Latest version at
+#!/usr/bin/env python3
# Copyright 2012, 2013 Ryan Sawhill Aroha
#
# License:
@@ -20,138 +15,153 @@
#
# You should have received a copy of the GNU General Public License
# along with Pyrite. If not, see .
-#
-#------------------------------------------------------------------------------
# StdLib:
-import gtk
-gtk.gdk.threads_init()
+import gi
+
+gi.require_version('GLib', '2.0')
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import GLib
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import Pango
+
+# gtk.gdk.threads_init()
import glib
-glib.threads_init()
+# glib.threads_init()
from threading import Thread
from sys import stderr
-from pango import FontDescription
from os import access, R_OK, read, close, pipe
-from os.path import isfile
-from urllib import url2pathname
+from os.path import isfile, exists
+from urllib.request import url2pathname
from shlex import split
from subprocess import check_output
from time import sleep
# Custom Modules:
-import cfg
-import prefs
-import crypt_interface
-from messages import MESSAGE_DICT
+from . import cfg
+from . import prefs
+from . import crypt_interface
+from .messages import MESSAGE_DICT
# Important variables
-SIGSTOP, SIGCONT = 19, 18
-TARGET_TYPE_URI_LIST = 80
-
+SIGSTOP, SIGCONT = 19, 18
+TARGET_TYPE_URI_LIST = 80
class Pyrite:
"""Display GTK+ window to interact with gpg or openssl via Xface object."""
-
-
- def show_errmsg(self, msg, dialog=gtk.MESSAGE_ERROR, parent=None):
+
+ def show_errmsg(self, msg, dialog=Gtk.MessageType.ERROR, parent=None):
"""Display msg with GtkMessageDialog."""
- d = gtk.MessageDialog(
- parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, dialog,
- gtk.BUTTONS_OK, msg)
+ d = Gtk.MessageDialog(
+ flags=0,
+ message_type=Gtk.MessageType.ERROR,
+ buttons=Gtk.ButtonsType.OK,
+ text=msg
+ )
d.run()
d.destroy()
-
-
+
def __init__(self, cmdlineargs):
- """Build GUI interface from XML, etc."""
-
+ """Build GUI interface from XML, etc."""
+ self.x = None
+ self.filemode_saved_buff = None
+ self.encdec_sig_state_active = False
+ self.encdec_sig_state_sensitive = False
+ self.canceled = False
+ self.paused = False
+
+ # local ui folder overrides system /usr/share/pyrite/
+ if exists('./ui'):
+ cfg.ASSETDIR = './'
# Use GtkBuilder to build our GUI from the XML file
- builder = gtk.Builder()
- try: builder.add_from_file(cfg.ASSETDIR + 'ui/main.glade')
+ builder = Gtk.Builder()
+ try:
+ builder.add_from_file(cfg.ASSETDIR + 'ui/main.glade')
except:
self.show_errmsg(
"Problem loading GtkBuilder UI definition file at:\n " +
cfg.ASSETDIR + "ui/main.glade\nCannot continue.")
raise
-
- #--------------------------------------------------------- GET WIDGETS!
+
+ # GET WIDGETS!
# Main window
- self.g_window = builder.get_object('window1')
+ self.g_window = builder.get_object('window1')
# Toolbars
- self.g_maintoolbar = builder.get_object('hbox1')
- self.g_modetoolbar = builder.get_object('hbox2')
- self.g_enctoolbar = builder.get_object('hbox3')
- self.g_sigtoolbar = builder.get_object('hbox4')
+ self.g_maintoolbar = builder.get_object('hbox1')
+ self.g_modetoolbar = builder.get_object('hbox2')
+ self.g_enctoolbar = builder.get_object('hbox3')
+ self.g_sigtoolbar = builder.get_object('hbox4')
# Menu items
- self.g_mclear = builder.get_object('mnu_clear')
- self.g_mopen = builder.get_object('mnu_open')
- self.g_msave = builder.get_object('mnu_save')
- self.g_mengine = builder.get_object('mnu_switchengine')
- self.g_mcut = builder.get_object('mnu_cut')
- self.g_mcopy = builder.get_object('mnu_copy')
- self.g_mpaste = builder.get_object('mnu_paste')
- self.g_mprefs = builder.get_object('mnu_preferences')
- self.g_wrap = builder.get_object('toggle_wordwrap')
- self.g_taskstatus = builder.get_object('toggle_taskstatus')
- self.g_taskverbose = builder.get_object('toggle_gpgverbose')
+ self.g_mclear = builder.get_object('mnu_clear')
+ self.g_mopen = builder.get_object('mnu_open')
+ self.g_msave = builder.get_object('mnu_save')
+ self.g_mengine = builder.get_object('mnu_switchengine')
+ self.g_mcut = builder.get_object('mnu_cut')
+ self.g_mcopy = builder.get_object('mnu_copy')
+ self.g_mpaste = builder.get_object('mnu_paste')
+ self.g_mprefs = builder.get_object('mnu_preferences')
+ self.g_wrap = builder.get_object('toggle_wordwrap')
+ self.g_taskstatus = builder.get_object('toggle_taskstatus')
+ self.g_taskverbose = builder.get_object('toggle_gpgverbose')
# Top action toolbar
- self.g_encrypt = builder.get_object('btn_encrypt')
- self.g_decrypt = builder.get_object('btn_decrypt')
- self.g_bclear = builder.get_object('btn_clear')
- self.g_progbar = builder.get_object('progressbar')
- self.g_cancel = builder.get_object('btn_cancel')
- self.g_pause = builder.get_object('btn_pause')
- self.g_slider = builder.get_object('opacity_slider')
+ self.g_encrypt = builder.get_object('btn_encrypt')
+ self.g_decrypt = builder.get_object('btn_decrypt')
+ self.g_bclear = builder.get_object('btn_clear')
+ self.g_progbar = builder.get_object('progressbar')
+ self.g_cancel = builder.get_object('btn_cancel')
+ self.g_pause = builder.get_object('btn_pause')
# Mode-setting toolbar
- self.g_signverify = builder.get_object('toggle_mode_signverify')
- self.g_chk_outfile = builder.get_object('toggle_sign_chooseoutput')
- self.g_encdec = builder.get_object('toggle_mode_encdec')
- self.g_symmetric = builder.get_object('toggle_mode_symmetric')
- self.g_asymmetric = builder.get_object('toggle_mode_asymmetric')
- self.g_advanced = builder.get_object('toggle_advanced')
+ self.g_signverify = builder.get_object('toggle_mode_signverify')
+ self.g_chk_outfile = builder.get_object('toggle_sign_chooseoutput')
+ self.g_encdec = builder.get_object('toggle_mode_encdec')
+ self.g_symmetric = builder.get_object('toggle_mode_symmetric')
+ self.g_asymmetric = builder.get_object('toggle_mode_asymmetric')
+ self.g_advanced = builder.get_object('toggle_advanced')
# Encryption toolbar
- self.g_passlabel = builder.get_object('label_entry_pass')
- self.g_pass = builder.get_object('entry_pass')
- self.g_reciplabel = builder.get_object('label_entry_recip')
- self.g_recip = builder.get_object('entry_recip')
- self.g_enctoself = builder.get_object('toggle_enctoself')
- self.g_cipherlabel = builder.get_object('label_combobox_cipher')
- self.g_cipher = builder.get_object('combobox_cipher')
+ self.g_passlabel = builder.get_object('label_entry_pass')
+ self.g_pass = builder.get_object('entry_pass')
+ self.g_reciplabel = builder.get_object('label_entry_recip')
+ self.g_recip = builder.get_object('entry_recip')
+ self.g_enctoself = builder.get_object('toggle_enctoself')
+ self.g_cipherlabel = builder.get_object('label_combobox_cipher')
+ self.g_cipher = builder.get_object('combobox_cipher')
# Middle input area
- self.g_bopen = builder.get_object('btn_open')
- self.g_bsave = builder.get_object('btn_save')
- self.g_bcopyall = builder.get_object('btn_copyall')
- self.g_msgtxtview = builder.get_object('textview1')
- self.buff = self.g_msgtxtview.get_buffer()
- self.vbox_ibar = builder.get_object('vbox_ibar')
- self.vbox_ibar2 = builder.get_object('vbox_ibar2')
- self.g_expander = builder.get_object('expander_filemode')
- self.g_chooserbtn = builder.get_object('btn_filechooser')
- self.g_plaintext = builder.get_object('toggle_plaintext')
- self.g_frame2 = builder.get_object('frame2')
- self.g_errtxtview = builder.get_object('textview2')
- self.buff2 = self.g_errtxtview.get_buffer()
+ self.g_bopen = builder.get_object('btn_open')
+ self.g_bsave = builder.get_object('btn_save')
+ self.g_bcopyall = builder.get_object('btn_copyall')
+ self.g_msgtxtview = builder.get_object('textview1')
+ self.buff = self.g_msgtxtview.get_buffer()
+ self.vbox_ibar = builder.get_object('vbox_ibar')
+ self.vbox_ibar2 = builder.get_object('vbox_ibar2')
+ self.g_expander = builder.get_object('expander_filemode')
+ self.g_chooserbtn = builder.get_object('btn_filechooser')
+ self.g_plaintext = builder.get_object('toggle_plaintext')
+ self.g_frame2 = builder.get_object('frame2')
+ self.g_errtxtview = builder.get_object('textview2')
+ self.buff2 = self.g_errtxtview.get_buffer()
# Signing toolbar
- self.g_signature = builder.get_object('toggle_signature')
- self.g_sigmode = builder.get_object('combobox_sigmode')
- self.g_digestlabel = builder.get_object('label_combobox_digest')
- self.g_digest = builder.get_object('combobox_digest')
- self.g_chk_defkey = builder.get_object('toggle_defaultkey')
- self.g_defaultkey = builder.get_object('entry_defaultkey')
+ self.g_signature = builder.get_object('toggle_signature')
+ self.g_sigmode = builder.get_object('combobox_sigmode')
+ self.g_digestlabel = builder.get_object('label_combobox_digest')
+ self.g_digest = builder.get_object('combobox_digest')
+ self.g_chk_defkey = builder.get_object('toggle_defaultkey')
+ self.g_defaultkey = builder.get_object('entry_defaultkey')
# Statusbar
- self.g_statusbar = builder.get_object('statusbar')
+ self.g_statusbar = builder.get_object('statusbar')
self.g_activityspin = builder.get_object('spinner1')
-
+
# Set app icon to something halfway-decent
- gtk.window_set_default_icon_name(gtk.STOCK_DIALOG_AUTHENTICATION)
-
+ Gtk.Window.set_default_icon_name("dialog-password")
+
# Connect signals
builder.connect_signals(self)
-
+
# Other class attributes
- self.ib_filemode = None
- self.engine = 'missing_backend'
- self.quiting = False
+ self.ib_filemode = None
+ self.engine = 'missing_backend'
+ self.quiting = False
self.working_widgets_filemode = [
self.g_mclear, self.g_mprefs, self.g_encrypt, self.g_decrypt, self.g_bclear,
self.g_modetoolbar, self.g_enctoolbar, self.g_expander, self.g_sigtoolbar]
@@ -160,37 +170,37 @@ def __init__(self, cmdlineargs):
self.g_modetoolbar, self.g_enctoolbar, self.g_expander, self.g_sigtoolbar,
self.g_mengine, self.g_bcopyall, self.g_bopen, self.g_mopen, self.g_bsave,
self.g_msave, self.g_mcut, self.g_mcopy, self.g_mpaste]
-
+
# Initialize main Statusbar
self.status = self.g_statusbar.get_context_id('main')
self.g_statusbar.push(self.status, "Enter message to encrypt/decrypt")
-
+
# Sensitivity for these GtkEntrys not defaulted to False in xml because
# that makes their icons stay insensitive-looking forever
- self.g_pass.set_sensitive (False)
- self.g_recip.set_sensitive (False)
+ self.g_pass.set_sensitive(False)
+ self.g_recip.set_sensitive(False)
- #------------------------------ LOAD PREFERENCES AND SET WIDGET STATES!
+ # LOAD PREFERENCES AND SET WIDGET STATES!
self.preferences = prefs.Preferences()
-
+
# Make a clone of preferences dictionary
self.p = self.preferences.p
-
+
# Launch gpg/openssl interface
if cmdlineargs and cmdlineargs.backend:
backend = cmdlineargs.backend
else:
backend = None
-
+
self.instantiate_xface(preferred=backend, startup=True)
-
- #------------------------------------------------ DRAG AND DROP FUNNESS
- dnd_list = [ ( 'text/uri-list', 0, TARGET_TYPE_URI_LIST ) ]
- self.g_msgtxtview.drag_dest_set(
- gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT,
- dnd_list, gtk.gdk.ACTION_COPY)
-
- #---------------------------------------------------- CMDLINE ARGUMENTS
+
+ # DRAG AND DROP FUNNESS
+ dnd_list = [('text/uri-list', 0, TARGET_TYPE_URI_LIST)]
+ # self.g_msgtxtview.drag_dest_set(
+ # Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT,
+ # dnd_list, Gdk.DragAction.COPY)
+
+ # CMDLINE ARGUMENTS
if cmdlineargs:
a = cmdlineargs
@@ -199,15 +209,18 @@ def __init__(self, cmdlineargs):
if a.direct_file:
self.g_chooserbtn.set_filename(a.input)
self.g_expander.set_expanded(True)
- elif a.text_input: self.buff.set_text(a.input)
- else: self.open_in_txtview(a.input)
-
+ elif a.text_input:
+ self.buff.set_text(a.input)
+ else:
+ self.open_in_txtview(a.input)
+
if self.engine not in 'OpenSSL':
if a.recipients:
self.g_recip.set_text(a.recipients)
self.g_asymmetric.set_active(True)
if a.symmetric:
- if a.recipients: self.g_advanced.set_active(True)
+ if a.recipients:
+ self.g_advanced.set_active(True)
self.g_symmetric.set_active(True)
if a.defaultkey:
self.g_defaultkey.set_text(a.defaultkey)
@@ -216,72 +229,71 @@ def __init__(self, cmdlineargs):
self.g_encdec.set_active(True)
elif a.signverify:
self.g_signverify.set_active(True)
-
- #--------------------------------------------------- OUR LOVELY COMM DEVICE
+
+ # OUR LOVELY COMM DEVICE
def infobar(self, id, filename=None, customtext=None, vbox=None):
"""Popup a new auto-hiding InfoBar."""
-
+
# Find the needed dictionary inside our message dict, by id
MSG = MESSAGE_DICT[id]
- # Use value from MSG type & icon to lookup Gtk constant, e.g. gtk.MESSAGE_INFO
- msgtype = cfg.MSGTYPES[ MSG['type'] ]
- imgtype = cfg.IMGTYPES[ MSG['icon'] ]
+ # Use value from MSG type & icon to lookup Gtk constant, e.g. Gtk.MessageType.INFO
+ msgtype = cfg.MSGTYPES[MSG['type']]
+ imgtype = cfg.IMGTYPES[MSG['icon']]
# Replace variables in message text & change text color
message = ("" +
MSG['text'].format(filename=filename, customtext=customtext) +
"")
-
+
# Now that we have all the data we need, START creating!
- ibar = gtk.InfoBar()
+ ibar = Gtk.InfoBar()
ibar.set_message_type(msgtype)
if vbox:
# If specific vbox requested: assume ibar for filemode, add cancel button
- ibar.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
- ibar.connect ('response', self.cleanup_filemode)
+ ibar.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
+ ibar.connect('response', self.cleanup_filemode)
else:
# If no specific vbox requested: do normal ibar at the top of message area
- vbox = self.vbox_ibar
- ibar.add_button (gtk.STOCK_OK, gtk.RESPONSE_OK)
- ibar.connect ('response', lambda *args: ibar.destroy())
- vbox.pack_end (ibar, False, False)
- content = ibar.get_content_area()
- img = gtk.Image()
- img.set_from_stock (imgtype, gtk.ICON_SIZE_LARGE_TOOLBAR)
- content.pack_start (img, False, False)
- img.show ()
- label = gtk.Label()
- label.set_markup (message)
- content.pack_start (label, False, False)
- label.show ()
+ vbox = self.vbox_ibar
+ ibar.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
+ ibar.connect('response', lambda *args: ibar.destroy())
+ vbox.pack_end(ibar, False, False, 0)
+ content = ibar.get_content_area()
+ img = Gtk.Image()
+ img.set_from_stock(imgtype, Gtk.IconSize.LARGE_TOOLBAR)
+ content.pack_start(img, False, False, 0)
+ img.show()
+ label = Gtk.Label()
+ label.set_markup(message)
+ content.pack_start(label, False, False, 0)
+ label.show()
# FIXME: Why doesn't Esc trigger this close signal?
- ibar.connect ('close', lambda *args: ibar.destroy())
+ ibar.connect('close', lambda *args: ibar.destroy())
ibar.show()
if MSG['timeout'] > 0:
- glib.timeout_add_seconds(MSG['timeout'], ibar.destroy)
+ GLib.timeout_add_seconds(MSG['timeout'], ibar.destroy)
return ibar
-
-
- #----------------------------------------------------- BRING UP GPG/OPENSSL
+
+ # BRING UP GPG/OPENSSL
def instantiate_xface(self, preferred=None, startup=False):
- """Instantiate Gpg or Openssl interface."""
-
- b = ['gpg2', 'gpg', 'openssl']
- # self.p['backend'] contains 0, 1, or 2, corresponding to the above items in b
- # Desired: convert the number setting to the human-readable name and store as b
- b = b[self.p['backend']]
-
+ """Instantiate Gpg or Openssl interface."""
+
# If we weren't passed preferred argument, set desired interface to backend pref
- if not preferred: preferred = b
-
+ if not preferred:
+ b = ['gpg', 'openssl']
+ # self.p['backend'] contains 0, 1, or 2, corresponding to the above items in b
+ # Desired: convert the number setting to the human-readable name and store as b
+ b = b[self.p['backend']]
+ preferred = b
+
# Loading gpg
- def gpg(backend_pref=b, fallback=False):
- self.x = crypt_interface.Gpg(firstchoice=backend_pref)
- self.engine = self.x.GPG_BINARY.upper()
+ def gpg(fallback=False):
+ self.x = crypt_interface.Gpg()
+ self.engine = 'GPG'
self.g_mengine.set_label("Use OpenSSL as Engine")
if fallback:
self.g_mengine.set_sensitive(False)
self.infobar('engine_openssl_missing')
-
+
# Loading openssl
def openssl(fallback=False):
self.x = crypt_interface.Openssl()
@@ -292,16 +304,20 @@ def openssl(fallback=False):
self.infobar('engine_gpg_missing')
else:
self.infobar('engine_openssl_notice')
-
+
# Setup for neutered-run (when missing all backends)
def err_allmissing():
self.infobar('engine_all_missing')
self.g_mengine.set_sensitive(False)
- for w in self.g_encrypt, self.g_decrypt: w.set_sensitive(False)
- class dummy: pass
+ for w in self.g_encrypt, self.g_decrypt:
+ w.set_sensitive(False)
+
+ class dummy:
+ pass
+
self.x = dummy()
self.x.io = dict(stdin='', stdout='', gstatus=0, infile=0, outfile=0)
-
+
# Get it done!
if preferred in 'openssl':
# If loading openssl, try that first, then fallback to gpg
@@ -321,153 +337,149 @@ class dummy: pass
openssl(fallback=True)
except:
err_allmissing()
-
+
self.g_window.set_title("Pyrite [{}]".format(self.engine))
-
+
self.buff2.set_text("Any output generated from calls to {} will be "
- "displayed here.\n\nIn the View menu you can change "
+ "displayed here.\n\n"
+ "In the View menu you can change "
"the verbosity level, hide this pane, or simply change "
- "the font size.".format(self.engine.lower()))
-
+ "the font size.".format(self.engine))
+
self.set_defaults_from_prefs(startup)
-
-
- #--------------------------------------------- SET OPMODES, ETC, FROM PREFS
+
+ # SET OPMODES, ETC, FROM PREFS
def set_defaults_from_prefs(self, startup=False):
"""Set window toggle states via preferences."""
-
+
if self.p['enctype'] == 0:
- self.g_symmetric.set_active (True)
+ self.g_symmetric.set_active(True)
elif self.p['enctype'] == 1:
- self.g_asymmetric.set_active (True)
+ self.g_asymmetric.set_active(True)
elif self.p['enctype'] == 2:
- self.g_advanced.set_active (True)
- self.g_symmetric.set_active (True)
- self.g_asymmetric.set_active (True)
-
+ self.g_advanced.set_active(True)
+ self.g_symmetric.set_active(True)
+ self.g_asymmetric.set_active(True)
+
if self.p['advanced']:
- self.g_advanced.set_active (True)
-
+ self.g_advanced.set_active(True)
+
if self.p['advanced'] or self.p['enctype'] > 0:
if self.p['addsig']:
- self.g_signature.set_active (True)
+ self.g_signature.set_active(True)
if self.p['enctoself']:
- self.g_enctoself.set_active (True)
-
+ self.g_enctoself.set_active(True)
+
if self.p['opmode']:
- self.g_signverify.set_active (True)
+ self.g_signverify.set_active(True)
if not self.g_expander.get_expanded():
- self.g_expander.set_expanded (self.p['expander'])
-
- self.g_cipher.set_active (self.p['cipher'])
- self.g_digest.set_active (self.p['digest'])
- self.g_chk_defkey.set_active (self.p['defkey'])
- self.g_defaultkey.set_text (self.p['defkeytxt'])
-
+ self.g_expander.set_expanded(self.p['expander'])
+
+ self.g_cipher.set_active(self.p['cipher'])
+ self.g_digest.set_active(self.p['digest'])
+ self.g_chk_defkey.set_active(self.p['defkey'])
+ self.g_defaultkey.set_text(self.p['defkeytxt'])
+
if startup:
- self.g_taskstatus.set_active (self.p['taskstatus'])
- self.g_taskverbose.set_active (self.p['verbose'])
- self.g_wrap.set_active (self.p['wrap'])
-
+ self.g_taskstatus.set_active(self.p['taskstatus'])
+ self.g_taskverbose.set_active(self.p['verbose'])
+ self.g_wrap.set_active(self.p['wrap'])
+
# Set TextView fonts, sizes, and colors
self.g_msgtxtview.modify_font(
- FontDescription("monospace {}".format(self.p['msgfntsize'])))
+ Pango.FontDescription("monospace {}".format(self.p['msgfntsize'])))
self.g_errtxtview.modify_font(
- FontDescription("normal {}".format(self.p['errfntsize'])))
+ Pango.FontDescription("normal {}".format(self.p['errfntsize'])))
+
+ bg_color = Gdk.Color(0, 0, 0)
+ bg_color.parse(self.p['color_bg'])
+ fg_color = Gdk.Color(0, 0, 0)
+ fg_color.parse(self.p['color_fg'])
self.g_msgtxtview.modify_base(
- gtk.STATE_NORMAL, gtk.gdk.color_parse(self.p['color_bg']))
+ Gtk.StateType.NORMAL, bg_color)
self.g_msgtxtview.modify_text(
- gtk.STATE_NORMAL, gtk.gdk.color_parse(self.p['color_fg']))
-
- if self.p['opc_slider']:
- self.g_slider.set_range (0, 100)
- self.g_slider.set_value (self.p['opacity'])
- self.g_slider.set_tooltip_text ("Change window opacity (current:{}%)".format(self.p['opacity']))
- self.g_slider.set_visible (True)
- else:
- self.g_window.set_opacity(self.p['opacity']/100.0)
-
+ Gtk.StateType.NORMAL, fg_color)
+
+
# These are all the widgets that can't be used in openssl mode
def setsensitive_gpgwidgets(x=True):
- self.g_signverify.set_sensitive (x)
- self.g_symmetric.set_sensitive (x)
- self.g_asymmetric.set_sensitive (x)
- self.g_advanced.set_sensitive (x)
- self.g_chk_defkey.set_sensitive (x)
- self.g_taskverbose.set_visible (x) # OpenSSL doesn't have verbosity
-
+ self.g_signverify.set_sensitive(x)
+ self.g_symmetric.set_sensitive(x)
+ self.g_asymmetric.set_sensitive(x)
+ self.g_advanced.set_sensitive(x)
+ self.g_chk_defkey.set_sensitive(x)
+ self.g_taskverbose.set_visible(x) # OpenSSL doesn't have verbosity
+
if self.engine in 'OpenSSL':
- self.g_encdec.set_active (True)
- self.g_symmetric.set_active (True)
- self.g_advanced.set_active (False)
- self.g_chk_defkey.set_active (False)
+ self.g_encdec.set_active(True)
+ self.g_symmetric.set_active(True)
+ self.g_advanced.set_active(False)
+ self.g_chk_defkey.set_active(False)
if startup or self.g_cipher.get_active() in {0, 2}:
# If starting up, or current cipher set to 'Default' or 'Twofish'
if self.p['cipher'] not in {0, 2}:
# Set cipher to preference unless pref is 'Default' or 'Twofish'
- self.g_cipher.set_active (self.p['cipher'])
+ self.g_cipher.set_active(self.p['cipher'])
else:
# Otherwise, set to AES
- self.g_cipher.set_active (1)
- setsensitive_gpgwidgets (False)
+ self.g_cipher.set_active(1)
+ setsensitive_gpgwidgets(False)
else:
- setsensitive_gpgwidgets (True)
-
-
- #--------------------------------------------------------- HELPER FUNCTIONS
-
- def fix_msgtxtviewcolor(self, sensitive):
+ setsensitive_gpgwidgets(True)
+
+ # HELPER FUNCTIONS
+
+ def fix_msgtxtview_color(self, sensitive):
"""Change Message area text to black when TextView insensitive."""
if sensitive:
+ fg_color = Gdk.Color(0, 0, 0)
+ fg_color.parse(self.p['color_fg'])
self.g_msgtxtview.modify_text(
- gtk.STATE_NORMAL, gtk.gdk.color_parse(self.p['color_fg']))
+ Gtk.StateType.NORMAL, fg_color)
else:
+ fg_color = Gdk.Color(0, 0, 0)
+ fg_color.parse('black')
self.g_msgtxtview.modify_text(
- gtk.STATE_NORMAL, gtk.gdk.color_parse('black'))
-
-
+ Gtk.StateType.NORMAL, fg_color)
+
def get_file_path_from_dnd_dropped_uri(self, uri):
path = ''
- if uri.startswith('file:\\\\\\'): # windows
+ if uri.startswith('file:\\\\\\'): # windows
path = uri[8:] # 8 is len('file:///')
- elif uri.startswith('file://'): # nautilus, rox
+ elif uri.startswith('file://'): # nautilus, rox
path = uri[7:] # 7 is len('file://')
- elif uri.startswith('file:'): # xffm
+ elif uri.startswith('file:'): # xffm
path = uri[5:] # 5 is len('file:')
path = url2pathname(path) # escape special chars
path = path.strip('\r\n\x00') # remove \r\n and NULL
return path
-
-
+
def set_stdstatus(self):
- """Set a standard mode-depenedent status message."""
+ """Set a standard mode-dependent status message."""
self.g_statusbar.pop(self.status)
if self.g_signverify.get_active():
s = "Enter message to sign or verify"
else:
s = "Enter message to encrypt or decrypt"
self.g_statusbar.push(self.status, s)
-
-
- def test_file_isbinary(self, filename):
+
+ def test_file_is_plain_text(self, filename):
"""Utilize nix file cmd to determine if filename is binary or text."""
cmd = split("file -b -e soft '{}'".format(filename))
- if check_output(cmd)[:4] in {'ASCI', 'UTF-'}:
- return False
- return True
-
-
+ output = check_output(cmd)
+ return output[:4] in (b'ASCI', b'UTF-')
+
def open_in_txtview(self, filename):
"""Replace contents of msg TextView's TextBuffer with contents of file."""
try:
- with open(filename) as f: self.buff.set_text(f.read())
+ with open(filename) as f:
+ self.buff.set_text(f.read())
if self.buff.get_char_count() < 1:
self.infobar('txtview_fileopen_binary_error')
except:
self.infobar('txtview_fileopen_error', filename)
-
-
+
# This is called when entering & exiting direct-file mode
def filemode_enablewidgets(self, x=True):
"""Enable/disable certain widgets due to working in direct-file mode."""
@@ -476,17 +488,15 @@ def filemode_enablewidgets(self, x=True):
self.g_mpaste, self.g_msgtxtview]
for w in widgets:
w.set_sensitive(x)
- self.fix_msgtxtviewcolor(x)
-
-
+ self.fix_msgtxtview_color(x)
+
# This is called when user tries to copyall, save, or {en,de}crypt/sign/verify
def test_msgbuff_isempty(self, message):
"""Return True + show infobar containing msg if Message area is empty."""
if self.buff.get_char_count() < 1:
self.infobar('txtview_empty', customtext=message)
return True
-
-
+
def confirm_overwrite_callback(self, chooser):
"""In filechooser, disallow output file being the input file."""
outfile = chooser.get_filename()
@@ -494,41 +504,46 @@ def confirm_overwrite_callback(self, chooser):
self.show_errmsg(
"Simultaneously reading from & writing to a file is a baaad idea. "
"Choose a different output filename.", parent=chooser)
- return gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN
+ return Gtk.FileChooserConfirmation.SELECT_AGAIN
else:
- return gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM
-
-
+ return Gtk.FileChooserConfirmation.CONFIRM
+
# Generic file chooser for opening or saving
def chooser_grab_filename(self, mode, save_suggestion=None):
"""Present file chooser dialog and return filename or None."""
-
+
filename = None
- if mode in 'open': title = "Choose text file to open as input..."
- elif mode in 'save': title = "Choose output filename..."
- cmd = ("gtk.FileChooserDialog('{0}', None, gtk.FILE_CHOOSER_ACTION_{1}, "
- "(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))"
+ if mode in 'open':
+ title = "Choose text file to open as input..."
+ elif mode in 'save':
+ title = "Choose output filename..."
+ cmd = ("Gtk.FileChooserDialog('{0}', None, Gtk.FileChooserAction.{1}, "
+ "(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))"
.format(title, mode.upper()))
chooser = eval(cmd)
-
+
if mode in 'open':
# Setup file filters
- t = gtk.FileFilter() ; t.set_name("Text Files") ; t.add_mime_type("text/*")
- a = gtk.FileFilter() ; a.set_name("All Files") ; a.add_pattern("*")
+ t = Gtk.FileFilter()
+ t.set_name("Text Files")
+ t.add_mime_type("text/*")
+ a = Gtk.FileFilter()
+ a.set_name("All Files")
+ a.add_pattern("*")
chooser.add_filter(t)
chooser.add_filter(a)
elif mode in 'save':
# Setup overwrite-confirmation cb + current filename
chooser.set_do_overwrite_confirmation(True)
chooser.connect('confirm-overwrite', self.confirm_overwrite_callback)
- if save_suggestion: chooser.set_current_name(save_suggestion)
-
- if chooser.run() == gtk.RESPONSE_OK:
+ if save_suggestion:
+ chooser.set_current_name(save_suggestion)
+
+ if chooser.run() == Gtk.ResponseType.OK:
filename = chooser.get_filename()
chooser.destroy()
return filename
-
-
+
def grab_activetext_combobox(self, combobox):
"""Return the text of active combobox selection."""
cbmodel = combobox.get_model()
@@ -537,72 +552,65 @@ def grab_activetext_combobox(self, combobox):
return None # If first choice is selected, i.e. 'Default'
else:
return cbmodel[cbindex][0]
-
-
+
# This is called by encrypt/decrypt buttons when operating in direct-file mode
def filemode_get_outfile(self, mode):
"""Use FileChooser to get an output filename for direct enc/dec."""
-
+
# Prompt for output file
outfile = self.chooser_grab_filename('save', self.x.io['infile'])
-
+
# Kick off processing unless user canceled
if outfile:
self.x.io['outfile'] = outfile
self.launchxface(mode)
-
-
+
def initiate_filemode(self):
- """Ensure read access of file set by chooserwidget and notify user of next steps."""
-
+ """Ensure read access of file set by chooser widget and notify user of next steps."""
+
# Prompt for filename but err out if file can't be read
infile = self.g_chooserbtn.get_filename()
if not access(infile, R_OK):
self.infobar('filemode_fileopen_error', infile)
return
-
+
# Tweak some widgets if in Sign/Verify mode
if self.g_signverify.get_active():
- self.g_chk_outfile.set_visible (True)
- self.g_chk_outfile.set_active (self.p['svoutfiles'])
- self.g_sigmode.set_active (self.p['file_sigmode'])
-
+ self.g_chk_outfile.set_visible(True)
+ self.g_chk_outfile.set_active(self.p['svoutfiles'])
+ self.g_sigmode.set_active(self.p['file_sigmode'])
+
# Configure state of plaintext output checkbox via user-settings
- self.g_plaintext.set_sensitive (True)
-
+ self.g_plaintext.set_sensitive(True)
+
if self.p['txtoutput'] == 0: # Autodetect
- if self.test_file_isbinary(infile):
- self.g_plaintext.set_active (False)
- else:
- self.g_plaintext.set_active (True)
-
+ self.g_plaintext.set_active(self.test_file_is_plain_text(infile))
elif self.p['txtoutput'] == 1: # Always Binary
- self.g_plaintext.set_active (False)
-
+ self.g_plaintext.set_active(False)
elif self.p['txtoutput'] == 2: # Always Text
- self.g_plaintext.set_active (True)
-
+ self.g_plaintext.set_active(True)
+
# Set statusbar w/ filemode status
self.g_statusbar.pop(self.status)
self.g_statusbar.push(self.status, "Choose an action to perform on {!r}".format(infile))
-
+
if self.ib_filemode:
# If filemode infobar already present: user picked a new file, so destroy old ibar
self.ib_filemode.destroy()
else:
# Otherwise, save TextView buffer for later and then blow it away
self.filemode_saved_buff = self.buff.get_text(self.buff.get_start_iter(),
- self.buff.get_end_iter())
+ self.buff.get_end_iter(),
+ False)
self.buff.set_text('')
self.filemode_enablewidgets(False)
-
+
# Create filemode infobar with cancel button
self.ib_filemode = self.infobar('filemode_blue_banner', infile, vbox=self.vbox_ibar2)
-
+
# Set input file
self.x.io['infile'] = infile
-
-
+
def cleanup_filemode(self, *args):
"""Revert the changes (to widgets, etc) that filemode causes."""
# Restore message buffer
@@ -612,40 +620,38 @@ def cleanup_filemode(self, *args):
self.ib_filemode.destroy()
self.ib_filemode = None
# Enable/sensitize widgets
- self.filemode_enablewidgets (True)
- self.g_chk_outfile.set_visible (False)
+ self.filemode_enablewidgets(True)
+ self.g_chk_outfile.set_visible(False)
# Ensure sigmode combobox is back to proper 'normal'
if self.g_signverify.get_active():
- self.g_sigmode.set_active (self.p['text_sigmode'])
+ self.g_sigmode.set_active(self.p['text_sigmode'])
else:
- self.g_sigmode.set_active (0)
+ self.g_sigmode.set_active(0)
# Set statusbar
self.set_stdstatus()
- #while gtk.events_pending():
- gtk.main_iteration()
+ # while Gtk.events_pending():
+ Gtk.main_iteration()
# Reset filenames
self.x.io['infile'] = 0
self.x.io['outfile'] = 0
# Disable plaintext CheckButton
- self.g_plaintext.set_sensitive (False)
- self.g_plaintext.set_active (True)
-
-
- #--------------------------------------------------- HERE BE GTK SIGNAL CBs
-
+ self.g_plaintext.set_sensitive(False)
+ self.g_plaintext.set_active(True)
+
+ # HERE BE GTK SIGNAL CBs
+
# Called by window destroy / Quit menu item
def action_quit(self, w):
"""Shutdown application and any child process."""
self.quiting = True
- if self.x.childprocess and self.x.childprocess.returncode == None:
+ if self.x.childprocess and self.x.childprocess.returncode is None:
if self.paused:
self.x.childprocess.send_signal(SIGCONT)
self.x.childprocess.terminate()
stderr.write("\n")
- #sleep(0.2)
- gtk.main_quit()
-
-
+ # sleep(0.2)
+ Gtk.main_quit()
+
# Called when dnd occurs on Message TextView
def action_drag_data_received(self, w, context, x, y, selection, target_type, timestamp):
"""Read dragged text file into Message area."""
@@ -655,17 +661,8 @@ def action_drag_data_received(self, w, context, x, y, selection, target_type, ti
path = self.get_file_path_from_dnd_dropped_uri(uri)
if isfile(path):
self.open_in_txtview(path)
-
-
- # Called by changing opacity hscale
- def action_opacity_slider(self, w):
- """Actions to perform when opacity scale is changed."""
- val = w.get_value()
- self.g_window.set_opacity(val/100.0)
- w.set_tooltip_text(
- "Change window opacity (current:{:.1f}%)".format(val))
-
-
+
+
# Called by SwitchEngine menu item
def action_switch_engine(self, w):
"""Switch backend between openssl & gpg."""
@@ -673,28 +670,26 @@ def action_switch_engine(self, w):
self.instantiate_xface('gpg')
else:
self.instantiate_xface('openssl')
-
-
+
# Called by About menu item
def action_about(self, w):
"""Launch About dialog."""
- builder = gtk.Builder()
- builder.add_from_file(cfg.ASSETDIR + 'ui/about.glade')
+ builder = Gtk.Builder()
+ builder.add_from_file(cfg.ASSETDIR + 'ui/about.glade')
about = builder.get_object('aboutdialog')
- about.set_logo_icon_name(gtk.STOCK_DIALOG_AUTHENTICATION)
- about.set_transient_for(self.g_window)
+ about.set_logo_icon_name(Gtk.STOCK_DIALOG_AUTHENTICATION)
+ # about.set_transient_for(self.g_window)
about.set_version(cfg.VERSION)
about.connect('response', lambda *args: about.destroy())
about.show()
-
-
+
# Called by Preferences menu item
def action_preferences(self, w):
"""Launch preferences window."""
-
+
# Run preferences window method from already-open pref instance
self.preferences.open_preferences_window(parentwindow=self.g_window)
-
+
# CB for pref window's save button
def savepref(*args):
# Attempt to save preferences
@@ -702,7 +697,7 @@ def savepref(*args):
# If success: destroy pref window, show infobar
self.preferences.window.destroy()
self.infobar('preferences_save_success', cfg.USERPREF_FILE)
-
+
# CB for pref window's apply button
def applypref(*args):
# Attempt to save preferences
@@ -710,15 +705,15 @@ def applypref(*args):
# If success, destroy pref window, import new prefs, show infobar
self.preferences.window.destroy()
self.p = self.preferences.p
- if self.x.io['infile']: self.cleanup_filemode()
+ if self.x.io['infile']:
+ self.cleanup_filemode()
self.instantiate_xface(startup=True)
self.infobar('preferences_apply_success', cfg.USERPREF_FILE)
-
+
# Connect signals
- self.preferences.btn_save.connect ('clicked', savepref)
- self.preferences.btn_apply.connect ('clicked', applypref)
-
-
+ self.preferences.btn_save.connect('clicked', savepref)
+ self.preferences.btn_apply.connect('clicked', applypref)
+
# Called by Clear toolbar btn or menu item
def action_clear(self, w):
"""Reset Statusbar, filemode stuff, TextView buffers."""
@@ -726,91 +721,83 @@ def action_clear(self, w):
self.cleanup_filemode()
else:
self.set_stdstatus()
- self.buff.set_text ('')
- self.buff2.set_text ('')
+ self.buff.set_text('')
+ self.buff2.set_text('')
self.x.io = dict(stdin='', stdout='', gstatus=0, infile=0, outfile=0)
-
-
+
# Called when user clicks the entry_icon in any of the entry widgets
def action_clear_entry(self, entry, *args):
"""Clear TextEntry widget."""
entry.set_text('')
-
-
+
# Called by Open toolbar btn or menu item
def action_open(self, w):
"""Read in a text file and push its contents to our TextView."""
filename = self.chooser_grab_filename('open')
if filename:
self.open_in_txtview(filename)
-
-
+
# Called when direct-file-mode FileChooserButton gets a new file set,
# either because of dnd or manual selection
def action_chooserbtn_file_set(self, w):
- print "[on_file-set] FileChooserButton.get_filename() output:\n{!r}\n".format(w.get_filename())
+ print("[on_file-set] FileChooserButton.get_filename() output:\n{!r}\n".format(w.get_filename()))
self.initiate_filemode()
-
-
+
# Called by Save toolbar btn or menu item
def action_save(self, w):
"""Save contents of msg TextView's TextBuffer to file."""
-
+
# If Message area is empty, err out
if self.test_msgbuff_isempty("No text to save."):
return
-
+
# Prompt for filename to save to; cancel if user cancels
filename = self.chooser_grab_filename('save')
if not filename:
return
-
+
# Set saving status
self.g_statusbar.push(self.status, "Saving {}".format(filename))
- #while gtk.events_pending():
- gtk.main_iteration()
-
+ # while Gtk.events_pending():
+ Gtk.main_iteration()
+
# Grab text from buffer
buffertext = self.buff.get_text(self.buff.get_start_iter(),
- self.buff.get_end_iter())
+ self.buff.get_end_iter(),
+ False)
try:
# If can open file for writing, show success infobar
- with open(filename, 'w') as f: f.write(buffertext)
+ with open(filename, 'w') as f:
+ f.write(buffertext)
self.infobar('txtview_save_success', filename)
except:
# Otherwise, show error
self.infobar('txtview_save_error', filename)
-
+
# Clear saving status
self.g_statusbar.pop(self.status)
-
-
+
def action_undo(self, w):
pass
-
-
+
def action_redo(self, w):
pass
-
-
+
# Called by Cut toolbar btn or menu item
def action_cut(self, w):
"""Cut msg TextBuffer selection."""
- self.buff.cut_clipboard(gtk.clipboard_get(), True)
-
-
+ self.buff.cut_clipboard(Gtk.clipboard_get(), True)
+
# Called by Copy toolbar btn or menu item
def action_copy(self, w):
"""Copy msg TextBuffer selection."""
- self.buff.copy_clipboard(gtk.clipboard_get())
-
-
+ self.buff.copy_clipboard(Gtk.clipboard_get())
+
# Called by Paste toolbar btn or menu item
def action_paste(self, w):
"""Paste clipboard into msg TextBuffer at selection."""
- self.buff.paste_clipboard(gtk.clipboard_get(), None, True)
-
-
+ self.buff.paste_clipboard(Gtk.clipboard_get(), None, True)
+
# Called by Copyall toolbar btn
def action_copyall(self, w):
"""Select whole msg TextBuffer contents and copy it to clipboard."""
@@ -818,10 +805,9 @@ def action_copyall(self, w):
return
self.buff.select_range(self.buff.get_start_iter(),
self.buff.get_end_iter())
- self.buff.copy_clipboard(gtk.clipboard_get())
+ self.buff.copy_clipboard(Gtk.clipboard_get())
self.infobar('txtview_copyall_success')
-
-
+
# Called by Zoom menu items
def action_zoom(self, w):
"""Increase/decrease font size of TextViews."""
@@ -833,11 +819,10 @@ def action_zoom(self, w):
self.p['msgfntsize'] -= 1
self.p['errfntsize'] -= 1
self.g_msgtxtview.modify_font(
- FontDescription("monospace {}".format(self.p['msgfntsize'])))
+ Pango.FontDescription("monospace {}".format(self.p['msgfntsize'])))
self.g_errtxtview.modify_font(
- FontDescription("normal {}".format(self.p['errfntsize'])))
-
-
+ Pango.FontDescription("normal {}".format(self.p['errfntsize'])))
+
# Called when Cipher combobox selection is changed
def action_cipher_changed(self, w):
"""Disallow certain cipher selections in OpenSSL mode."""
@@ -851,16 +836,10 @@ def action_cipher_changed(self, w):
self.infobar('cipher_openssl_no_twofish')
elif cipher in 'AES':
self.infobar('cipher_openssl_aes_note')
-
-
+
# Called by Encrypt/Sign toolbar btn
def action_encrypt(self, w):
"""Encrypt or sign input."""
- # DEBUG
- #self.g_chooserbtn.select_filename('/etc/passwd')
- #self.g_expander.set_expanded(True)
- #gtk.main_iteration()
- #return
if self.g_signverify.get_active():
# If in sign-only mode, figure out which sig-type
if self.g_sigmode.get_active() == 0:
@@ -873,8 +852,7 @@ def action_encrypt(self, w):
else:
# Normal enc/dec mode
self.launchxface('enc')
-
-
+
# Called by Decrypt/Verify toolbar btn
def action_decrypt(self, w):
"""Decrypt or verify input."""
@@ -884,232 +862,210 @@ def action_decrypt(self, w):
else:
# Normal enc/dec mode
self.launchxface('dec')
-
-
+
# Called by Symmetric checkbox toggle
def action_toggle_symmetric(self, w):
"""Toggle symmetric encryption (enable/disable certain widgets)."""
-
+
symm_widgets = [self.g_passlabel, self.g_pass]
-
+
if w.get_active():
# If entering toggled state, allow pass entry
for widget in symm_widgets:
- widget.set_sensitive (True)
+ widget.set_sensitive(True)
if not self.g_advanced.get_active():
# If not in advanced mode, disable Asymmetric
- self.g_asymmetric.set_active (False)
-
+ self.g_asymmetric.set_active(False)
+
else:
# If leaving toggled state, hide pass entry
for widget in symm_widgets:
- widget.set_sensitive (False)
+ widget.set_sensitive(False)
if not self.g_asymmetric.get_active():
# If unchecking Symm & Asymm isn't already on, turn it on
- self.g_asymmetric.set_active (True)
-
-
+ self.g_asymmetric.set_active(True)
+
# Called by Asymmetric checkbox toggle
def action_toggle_asymmetric(self, w):
"""Toggle asymmetric encryption (enable/disable certain widgets)."""
-
+
asymm_widgets = [self.g_reciplabel, self.g_recip, self.g_enctoself]
-
+
if w.get_active():
# If entering toggled state, allow recip entry, enctoself
for widget in asymm_widgets:
- widget.set_sensitive (True)
- self.g_signature.set_sensitive (True)
+ widget.set_sensitive(True)
+ self.g_signature.set_sensitive(True)
if not self.g_advanced.get_active():
# If not in advanced mode, disable Symmetric
- self.g_symmetric.set_active (False)
-
+ self.g_symmetric.set_active(False)
+ self.load_recipients_autocmplete()
else:
# If leaving toggled state, hide recip entry, enctoself
for widget in asymm_widgets:
- widget.set_sensitive (False)
- self.g_enctoself.set_active (False)
+ widget.set_sensitive(False)
+ self.g_enctoself.set_active(False)
if not self.g_advanced.get_active():
# If not in advanced mode, ensure add signature is unchecked
- self.g_signature.set_sensitive (False)
- self.g_signature.set_active (False)
+ self.g_signature.set_sensitive(False)
+ self.g_signature.set_active(False)
if not self.g_symmetric.get_active():
# If unchecking Asymm & Symm isn't already on, turn it on
- self.g_symmetric.set_active (True)
-
-
+ self.g_symmetric.set_active(True)
+
# Called by Advanced checkbox toggle
def action_toggle_advanced(self, w):
"""Enable/disable encryption widgets for advanced mode."""
-
+
if w.get_active():
# If entering the toggled state, allow adding signature
- self.g_signature.set_sensitive (True)
-
+ self.g_signature.set_sensitive(True)
+
else:
# If leaving the toggled state...
if self.g_symmetric.get_active():
# We have some things to do if Symmetric is checked...
if self.g_asymmetric.get_active():
# If Asymmetric is also checked, disable it
- self.g_asymmetric.set_active (False)
+ self.g_asymmetric.set_active(False)
else:
# If Asymmetric isn't checked, ensure addsig is disabled
- self.g_signature.set_sensitive (False)
- self.g_signature.set_active (False)
-
-
+ self.g_signature.set_sensitive(False)
+ self.g_signature.set_active(False)
+
# Called by Sign/Verify radio toggle
def action_toggle_mode_signverify(self, w):
"""Hide/show, change some widgets when switching modes."""
-
+
enc_widgets = [self.g_symmetric, self.g_asymmetric, self.g_advanced, self.g_enctoolbar]
-
+
# Change statusbar
self.set_stdstatus()
-
+
if w.get_active():
# If entering the toggled state: modify buttons, hide & show widgets
# Modify Encrypt/Decrypt button labels
- self.g_encrypt.set_label ("Sign")
- self.g_decrypt.set_label ("Verify")
+ self.g_encrypt.set_label("Sign")
+ self.g_decrypt.set_label("Verify")
# Hide encryption toolbar & Symmetric, Asymmetric, Adv toggles
for widget in enc_widgets:
- widget.set_visible (False)
+ widget.set_visible(False)
# Save state of AddSignature for switching back to Enc/Dec mode
self.encdec_sig_state_sensitive = self.g_signature.get_sensitive()
- self.encdec_sig_state_active = self.g_signature.get_active()
+ self.encdec_sig_state_active = self.g_signature.get_active()
# Desensitize AddSignature checkbox and turn it on
- self.g_signature.set_sensitive (False)
- self.g_signature.set_active (True)
+ self.g_signature.set_sensitive(False)
+ self.g_signature.set_active(True)
# Sensitize sigmode combobox
- self.g_sigmode.set_sensitive (True)
+ self.g_sigmode.set_sensitive(True)
# Set sigmode combobox via user prefs
if self.x.io['infile']:
- self.g_sigmode.set_active (self.p['file_sigmode'])
- self.g_chk_outfile.set_visible (True)
+ self.g_sigmode.set_active(self.p['file_sigmode'])
+ self.g_chk_outfile.set_visible(True)
else:
- self.g_sigmode.set_active (self.p['text_sigmode'])
-
+ self.g_sigmode.set_active(self.p['text_sigmode'])
+
else:
# If leaving the toggled state, we have some things to reverse
- self.g_encrypt.set_label ("_Encrypt")
- self.g_decrypt.set_label ("_Decrypt")
- self.g_chk_outfile.set_visible (False)
+ self.g_encrypt.set_label("_Encrypt")
+ self.g_decrypt.set_label("_Decrypt")
+ self.g_chk_outfile.set_visible(False)
for widget in enc_widgets:
- widget.set_visible (True)
- self.g_signature.set_sensitive (self.encdec_sig_state_sensitive)
- self.g_signature.set_active (self.encdec_sig_state_active)
- self.g_sigmode.set_sensitive (False)
- self.g_sigmode.set_active (0) # Reset to 'Embedded' type for Enc/Dec mode
-
-
+ widget.set_visible(True)
+ self.g_signature.set_sensitive(self.encdec_sig_state_sensitive)
+ self.g_signature.set_active(self.encdec_sig_state_active)
+ self.g_sigmode.set_sensitive(False)
+ self.g_sigmode.set_active(0) # Reset to 'Embedded' type for Enc/Dec mode
+
# Called by 'Change Default Key' checkbox toggle
def action_toggle_defaultkey(self, w):
"""Hide/show Entry widget for setting gpg 'localuser' argument."""
-
- if w.get_active():
- # If entering toggled state, show default key TextEntry
- self.g_defaultkey.set_visible (True)
- else:
- # If leaving toggled state, hide default key TextEntry
- self.g_defaultkey.set_visible (False)
-
-
+
+ # If entering toggled state, show default key TextEntry
+ self.g_defaultkey.set_visible(w.get_active())
+
# Called by 'Add Signature' checkbox toggle
def action_toggle_signature(self, w):
"""Hide/show some widgets when toggling adding of a signature to input."""
sig_widgets = [self.g_sigmode, self.g_digest, self.g_digestlabel]
-
- if w.get_active():
- # If entering toggled state, show sig toolbar widgets
- for widget in sig_widgets:
- widget.set_visible (True)
- else:
- # If leaving toggled state, hide sig toolbar widgets
- for widget in sig_widgets:
- widget.set_visible (False)
-
-
+
+ active = w.get_active()
+ # If entering toggled state, show sig toolbar widgets
+ for widget in sig_widgets:
+ widget.set_visible(active)
+
# Called by 'Task Status Side Panel' checkbox toggle
def action_toggle_taskstatus(self, w):
"""Show/hide side pane containing gpg stderr output."""
- if w.get_active():
- # If entering toggled state, show Task Status TextView frame
- self.g_frame2.set_visible (True)
- else:
- # If leaving toggled state, hide Task Status TextView frame
- self.g_frame2.set_visible (False)
-
-
+ # If entering toggled state, show Task Status TextView frame
+ self.g_frame2.set_visible(w.get_active())
+
# Called by 'Text Wrapping' checkbox toggle
def action_toggle_wordwrap(self, w):
"""Toggle word wrapping for main message TextView."""
if w.get_active():
# If entering toggled state, enable word wrapping
- self.g_msgtxtview.set_wrap_mode(gtk.WRAP_WORD)
+ self.g_msgtxtview.set_wrap_mode(Gtk.WrapMode.WORD)
else:
# If leaving toggled state, disable word wrapping
- self.g_msgtxtview.set_wrap_mode(gtk.WRAP_NONE)
-
-
+ self.g_msgtxtview.set_wrap_mode(Gtk.WrapMode.NONE)
+
# Called by [processing progbar] Cancel button
def action_cancel_child_process(self, btn):
"""Terminate gpg/openssl subprocess."""
-
- stderr.write ("Canceling Operation\n")
- self.canceled = True
+
+ stderr.write("Canceling Operation\n")
+ self.canceled = True
for w in self.g_cancel, self.g_pause:
- w.set_sensitive (False)
- self.g_progbar.set_text ("Canceling Operation...")
+ w.set_sensitive(False)
+ self.g_progbar.set_text("Canceling Operation...")
self.g_activityspin.stop()
- gtk.main_iteration()
+ Gtk.main_iteration()
while not self.x.childprocess:
- gtk.main_iteration()
+ Gtk.main_iteration()
if self.paused:
self.x.childprocess.send_signal(SIGCONT)
self.x.childprocess.terminate()
self.show_working_progress(False)
-
-
+
# Called by [processing progbar] Pause button
def action_pause_child_process(self, btn):
"""Suspend/resume gpg/openssl subprocess with SIGSTOP/SIGCONT."""
-
+
# We can't pause childprocess until it actually starts
while not self.x.childprocess:
- gtk.main_iteration()
-
+ Gtk.main_iteration()
+
if self.paused:
# Already paused, so, time to unpause
- stderr.write ("\n")
- self.paused = False
- btn.set_relief (gtk.RELIEF_NONE)
- self.g_progbar.set_text ("{} working...".format(self.engine))
+ stderr.write("\n")
+ self.paused = False
+ btn.set_relief(Gtk.ReliefStyle.NONE)
+ self.g_progbar.set_text("{} working...".format(self.engine))
self.g_activityspin.start()
self.x.childprocess.send_signal(SIGCONT)
else:
# Time to pause
- stderr.write ("\n")
- self.paused = True
- btn.set_relief (gtk.RELIEF_NORMAL)
- self.g_progbar.set_text ("Operation PAUSED")
+ stderr.write("\n")
+ self.paused = True
+ btn.set_relief(Gtk.ReliefStyle.NORMAL)
+ self.g_progbar.set_text("Operation PAUSED")
self.g_activityspin.stop()
self.x.childprocess.send_signal(SIGSTOP)
-
-
- #------------------------------------------------------ MAIN XFACE FUNCTION
+
+ # MAIN XFACE FUNCTION
def launchxface(self, action):
"""Manage I/O between Gtk objects and our GpgXface or OpensslXface object."""
- self.canceled = False
- self.paused = False
+ # User Canceled
+ self.canceled = False
+ self.paused = False
self.x.childprocess = None
-
- ### PREPARE Xface ARGS
- passwd = None
- recip = None
- localuser = None
+
+ # PREPARE Xface ARGS
+ passwd = None
+ recip = None
+ localuser = None
# symmetric & passwd
symmetric = self.g_symmetric.get_active()
if symmetric:
@@ -1119,7 +1075,7 @@ def launchxface(self, action):
self.infobar('x_missing_passphrase')
return
passwd = None # If passwd was '' , set to None, which will trigger gpg-agent if necessary
-
+
# INTERLUDE: If operating in textinput mode, check for input text
if not self.x.io['infile']:
# Make sure textview has a proper message in it
@@ -1127,15 +1083,16 @@ def launchxface(self, action):
return False
# Make TextView immutable to changes
self.g_msgtxtview.set_sensitive(False)
- self.fix_msgtxtviewcolor(False)
-
+ self.fix_msgtxtview_color(False)
+
# enctoself
enctoself = self.g_enctoself.get_active()
# recip
asymmetric = self.g_asymmetric.get_active()
if asymmetric:
recip = self.g_recip.get_text()
- if not recip: recip = None # If recip was '' , set to None
+ if not recip:
+ recip = None # If recip was '' , set to None
# cipher, base64
cipher = self.grab_activetext_combobox(self.g_cipher)
base64 = self.g_plaintext.get_active()
@@ -1154,11 +1111,11 @@ def launchxface(self, action):
# localuser
if self.g_chk_defkey.get_active():
localuser = self.g_defaultkey.get_text()
- if not localuser: localuser = None
-
+ if not localuser:
+ localuser = None
+
# INITIAL FILE INPUT MODE PREP
if self.x.io['infile'] and not self.x.io['outfile']:
-
if base64 or action in 'clearsign':
outfile = self.x.io['infile'] + '.asc'
elif self.engine in 'OpenSSL':
@@ -1167,10 +1124,10 @@ def launchxface(self, action):
outfile = self.x.io['infile'] + '.sig'
else:
outfile = self.x.io['infile'] + '.gpg'
-
+
if action in 'dec':
outfile = self.x.io['infile'][:-4]
-
+
if action not in 'verify':
if self.g_signverify.get_active() and not self.g_chk_outfile.get_active():
pass
@@ -1182,113 +1139,103 @@ def launchxface(self, action):
return
working_widgets = self.working_widgets_filemode
- for w in working_widgets: w.set_sensitive(False)
+ for w in working_widgets:
+ w.set_sensitive(False)
self.ib_filemode.hide()
-
+
# FILE INPUT MODE PREP WHEN ALREADY HAVE OUTPUT FILE
elif self.x.io['infile'] and self.x.io['outfile']:
-
working_widgets = self.working_widgets_filemode
- for w in working_widgets: w.set_sensitive(False)
+ for w in working_widgets:
+ w.set_sensitive(False)
self.ib_filemode.hide()
-
+
# TEXT INPUT MODE PREP
else:
-
working_widgets = self.working_widgets_textmode
- for w in working_widgets: w.set_sensitive(False)
-
+ for w in working_widgets:
+ w.set_sensitive(False)
# Save textview buffer to Xface stdin
self.x.io['stdin'] = self.buff.get_text(self.buff.get_start_iter(),
- self.buff.get_end_iter())
-
+ self.buff.get_end_iter(),
+ False)
+
# Set working status + spinner + progress bar
self.show_working_progress(True, action)
# Clear Task Status
self.buff2.set_text('')
-
+
# Setup stderr file descriptors & update task status while processing
self.x.io['stderr'] = pipe()
- glib.io_add_watch(
+ GLib.io_add_watch(
self.x.io['stderr'][0],
- glib.IO_IN | glib.IO_HUP,
+ GLib.IOCondition.IN | GLib.IOCondition.HUP,
self.update_task_status)
-
+
if self.engine in 'OpenSSL':
# ATTEMPT EN-/DECRYPTION w/OPENSSL
Thread(
target=self.x.openssl,
args=(action, passwd, base64, cipher)
- ).start()
-
+ ).start()
+
else:
# GPG
if verbose:
# Setup gpg-status file descriptors & update terminal while processing
self.x.io['gstatus'] = pipe()
- glib.io_add_watch(
+ GLib.io_add_watch(
self.x.io['gstatus'][0],
- glib.IO_IN | glib.IO_HUP,
+ GLib.IOCondition.IN | GLib.IOCondition.HUP,
self.update_task_status, 'term')
# ATTEMPT EN-/DECRYPTION w/GPG
Thread(
target=self.x.gpg,
args=(action, encsign, digest, localuser, base64, symmetric, passwd,
asymmetric, recip, enctoself, cipher, verbose, alwaystrust)
- ).start()
-
+ ).start()
+
# Wait for subprocess to finish or for Cancel button to be clicked
c = 0
- while not self.x.childprocess or self.x.childprocess.returncode == None:
- if self.canceled: break
+ while not self.x.childprocess or self.x.childprocess.returncode is None:
+ if self.canceled:
+ break
if c % 15 == 0 and not self.paused:
self.g_progbar.pulse()
- gtk.main_iteration()
+ Gtk.main_iteration()
c += 1
if self.quiting:
# If application is shutting down
return
# Restore widgets to normal states
- for w in working_widgets: w.set_sensitive(True)
+ for w in working_widgets:
+ w.set_sensitive(True)
self.show_working_progress(False)
-
+
# FILE INPUT MODE CLEANUP
if self.x.io['infile']:
-
if self.canceled: # User Canceled!
-
self.ib_filemode.show()
-
if action in {'enc', 'dec'}:
action = "{}rypt".format(action.title())
elif action in {'embedsign', 'clearsign', 'detachsign'}:
action = "Sign"
elif action in 'verify':
action = action.title()
-
self.infobar('x_canceled_filemode', customtext=action)
-
elif self.x.childprocess.returncode == 0: # File Success!
-
if self.engine in 'OpenSSL' and action in 'enc':
self.infobar('x_opensslenc_success_filemode', self.x.io['outfile'], cipher)
-
elif action in {'enc', 'dec'}:
self.infobar('x_crypt_success_filemode', self.x.io['outfile'], action)
-
elif action in {'embedsign', 'clearsign'}:
self.infobar('x_sign_success_filemode', self.x.io['outfile'])
-
elif action in 'detachsign':
self.infobar('x_detachsign_success_filemode', self.x.io['outfile'])
-
elif action in 'verify':
self.infobar('x_verify_success')
-
self.cleanup_filemode()
-
else: # File Fail!
-
self.ib_filemode.show()
if action in 'verify':
self.infobar('x_verify_failed')
@@ -1301,38 +1248,31 @@ def launchxface(self, action):
elif action in {'embedsign', 'clearsign', 'detachsign'}:
action = 'sign'
self.infobar('x_generic_failed_filemode', customtext=action)
-
+
# TEXT INPUT MODE CLEANUP
else:
-
self.set_stdstatus()
self.g_msgtxtview.set_sensitive(True)
- self.fix_msgtxtviewcolor(True)
-
+ self.fix_msgtxtview_color(True)
if self.canceled: # User Canceled!
-
if action in {'enc', 'dec'}:
action = "{}rypt".format(action.title())
elif action in {'embedsign', 'clearsign', 'detachsign'}:
action = "Sign"
elif action in 'verify':
action = action.title()
-
self.infobar('x_canceled_textmode', customtext=action)
-
elif self.x.childprocess.returncode == 0: # Text Success!
-
if action in 'verify':
self.infobar('x_verify_success')
else:
# Set TextBuffer to gpg stdout
- self.buff.set_text(self.x.io['stdout'])
+ b = self.x.io['stdout'].decode('utf-8')
+ self.buff.set_text(b)
self.x.io['stdout'] = 0
if self.engine in 'OpenSSL' and action in 'enc':
self.infobar('x_opensslenc_success_textmode', customtext=cipher)
-
else: # Text Fail!
-
if action in 'verify':
self.infobar('x_verify_failed')
return
@@ -1344,43 +1284,41 @@ def launchxface(self, action):
elif action in {'embedsign', 'clearsign', 'detachsign'}:
action = 'sign'
self.infobar('x_generic_failed_textmode', customtext=action)
-
-
- #------------------------------------------ HELPERS FOR MAIN XFACE FUNCTION
-
- # CB for glib.io_add_watch()
+
+ # HELPERS FOR MAIN XFACE FUNCTION
+
+ # CB for GLib.io_add_watch()
def update_task_status(self, fd, condition, output='task'):
"""Read data waiting in file descriptor; close fd if other end hangs up."""
-
+
# If there's data to be read, let's read it
- if condition == glib.IO_IN:
+ if condition == GLib.IOCondition.IN:
+ b = read(fd, 1024).decode('utf-8')
if output in 'task':
# Output to Task Status
- self.buff2.insert(self.buff2.get_end_iter(), read(fd, 1024))
+ self.buff2.insert(self.buff2.get_end_iter(), b)
else:
# Output to stderr (will show if run from terminal)
- stderr.write(read(fd, 1024))
+ stderr.write(b)
return True
-
# If other end of pipe hangs up, close our fd and destroy the watcher
- elif condition == glib.IO_HUP:
+ elif condition == GLib.IOCondition.HUP:
if output in 'term':
stderr.write("\n")
close(fd)
return False
-
-
+
# Called when gpg/openssl begins and ends processing
def show_working_progress(self, show=True, action=None):
"""Hide/show progress widgets; set/unset working status + activity spinner."""
-
+
# Show/hide progress bar & its buttons
for w in self.g_progbar, self.g_cancel, self.g_pause:
w.set_visible(show)
-
+
if show:
# If beginning processing: set progbar text + working status, start spinner
- self.g_progbar.set_text ("{} working...".format(self.engine))
+ self.g_progbar.set_text("{} working...".format(self.engine))
if action in {'embedsign', 'clearsign', 'detachsign'}:
status = "Signing input ..."
elif action in 'verify':
@@ -1390,26 +1328,53 @@ def show_working_progress(self, show=True, action=None):
self.g_statusbar.push(self.status, status)
self.g_activityspin.set_visible(True)
self.g_activityspin.start()
- gtk.main_iteration()
-
+ Gtk.main_iteration()
else:
# If finished processing: ensure progbar buttons are normal, reset status, stop spinner
for w in self.g_cancel, self.g_pause:
w.set_sensitive(True)
- w.set_relief(gtk.RELIEF_NONE)
+ w.set_relief(Gtk.ReliefStyle.NONE)
self.g_activityspin.stop()
self.g_activityspin.set_visible(False)
self.g_statusbar.pop(self.status)
-
-
- #---------------------------------------------- RUN MAIN APPLICATION WINDOW
- def main(self):
- """Show main window, tweak some GTK+ settings and start GTK+ main loop."""
- self.g_window.show()
- settings = gtk.settings_get_default()
- settings.props.gtk_button_images = True
- with gtk.gdk.lock:
- gtk.main()
+ def loadmails_string_list(self):
+ """Return emails from all known keys."""
+ mails = list()
+ if self.engine == 'OpenSSL':
+ return mails
+ cmd = split("gpg --list-public-keys --with-colons")
+ keys_string = check_output(cmd).decode('utf-8')
+ keys_all = keys_string.split('\n')
+ for line in keys_all:
+ line_fields = line.split(':')
+ if line_fields[0] == 'uid':
+ name_email = line_fields[9]
+ mails.append(name_email)
+ return mails
+
+ # Loading names and emails for the recipient menu completion
+ def load_recipients_autocmplete(self):
+ mails = Gtk.ListStore(str)
+ for mail in self.loadmails_string_list():
+ mails.append([mail])
+ completion = Gtk.EntryCompletion()
+ completion.set_model(mails)
+ completion.set_text_column(0)
+ completion.set_match_func(self.recipient_contains, None)
+ self.g_recip.set_completion(completion)
+
+ def recipient_contains(self, completion, key_string, iter, data):
+ model = completion.get_model()
+ # get the completion strings
+ modelstr = model[iter][0]
+ return key_string in modelstr
+ # RUN MAIN APPLICATION WINDOW
+ def main(self):
+ """Show main window, and start GTK+ main loop."""
+ self.g_window.show()
+ self.g_window.connect("destroy", Gtk.main_quit)
+ self.g_window.show_all()
+ Gtk.main()
diff --git a/modules/crypt_interface.py b/pyrite/crypt_interface.py
similarity index 63%
rename from modules/crypt_interface.py
rename to pyrite/crypt_interface.py
index b553809..1e25779 100644
--- a/modules/crypt_interface.py
+++ b/pyrite/crypt_interface.py
@@ -1,9 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of Pyrite.
-# Last file mod: 2013/09/15
-# Latest version at
+#!/usr/bin/env python3
# Copyright 2012, 2013 Ryan Sawhill Aroha
#
# License:
@@ -20,30 +15,26 @@
#
# You should have received a copy of the GNU General Public License
# along with Pyrite. If not, see .
-#
-#------------------------------------------------------------------------------
-from sys import stderr
from os import pipe, write, close
from shlex import split
from subprocess import Popen, PIPE, check_output
+from sys import stderr
from time import sleep
-
-def flatten_list_to_stderr(list):
+def flatten_list_to_stderr(lines):
stderr.write("-" * 79 + "\n")
- for item in list:
+ for item in lines:
stderr.write(item + " ")
stderr.write("\n\n")
-
-class Gpg():
- """GPG/GPG2 interface for encryption/decryption/signing/verifying.
+class Gpg:
+ """GPG interface for encryption/decryption/signing/verifying.
- First thing: use subprocess module to call a gpg or gpg2 process, ensuring
- that one of them is available on the system; if not, of course we have to
+ First thing: use subprocess module to call a gpg process, ensuring
+ that it is available on the system; if not, of course we have to
quit (raise exception). Either way, that's all for __init__.
See the docstring for the main method -- gpg() -- for next steps.
@@ -51,79 +42,60 @@ class Gpg():
Security: Xface.gpg() can take a passphrase for symmetric enc/dec as
an argument, but it never stores that passphrase on disk; the passphrase is
passed to gpg via an os file descriptor. If any access to your secret key is
- required, gpg() invokes gpg/gpg2 with gpg-agent enabled.
+ required, gpg() invokes gpg with gpg-agent enabled.
"""
-
-
- def __init__(self, show_version=True, firstchoice='gpg2'):
- """Confirm we can run gpg or gpg2."""
-
- def gpg1():
+
+ def __init__(self, show_version=True):
+ """Confirm we can run gpg."""
+
+ try:
self.vers = Popen(['gpg', '--version'], stdout=PIPE).communicate()[0]
- self.GPG_BINARY = 'gpg'
-
- def gpg2():
- self.vers = Popen(['gpg2', '--version'], stdout=PIPE).communicate()[0]
- self.GPG_BINARY = 'gpg2'
-
- if firstchoice == 'gpg':
- try: gpg1()
- except:
- try: gpg2()
- except:
- stderr.write("gpg, gpg2 not found on your system.\n\n")
- raise
- else:
- try: gpg2()
- except:
- try: gpg1()
- except:
- stderr.write("gpg, gpg2 not found on your system.\n\n")
- raise
-
+ except:
+ stderr.write("gpg not found on your system.\n\n")
+ raise
+
# To show or not to show version info
if show_version:
stderr.write("{}\n".format(self.vers))
-
+
# I/O dictionary obj
self.io = dict(
- stdin='', # Stores input text for subprocess
+ stdin='', # Stores input text for subprocess
stdout='', # Stores stdout stream from subprocess
- stderr=0, # Stores tuple of r/w file descriptors for stderr stream
+ stderr=0, # Stores tuple of r/w file descriptors for stderr stream
gstatus=0, # Stores tuple of r/w file descriptors for gpg-status stream
- infile=0, # Input filename for subprocess
+ infile=0, # Input filename for subprocess
outfile=0) # Output filename for subprocess
-
+
self.childprocess = None
-
-
+
# Main gpg interface method
def gpg(
- self,
- action= None, # One of: enc, dec, embedsign, clearsign, detachsign, verify
- encsign= False, # Add '--sign' when encrypting?
- digest= None, # One of: sha256, sha1, etc; None == use gpg defaults
- localuser= None, # Value passed to --local-user to set default key for signing, etc
- base64= True, # Add '--armor' when encrypting/signing?
- symmetric= False, # Add '--symmetric'?
- passwd= None, # Passphrase for symmetric
- asymmetric= False, # Add '--encrypt'?
- recip= None, # Recipients for asymmetric (semicolon-delimited)
- enctoself= False, # Add first id from secret keyring as recipient?
- cipher= None, # One of: aes256, 3des, etc; None == use gpg defaults
- verbose= False, # Add '--verbose'?
- alwaystrust=False, # Add '--trust-model always'?
- yes= True # Add '--yes'? (will overwrite files)
- ):
- """Build a gpg cmdline and then launch gpg/gpg2, saving output appropriately.
+ self,
+ action=None, # One of: enc, dec, embedsign, clearsign, detachsign, verify
+ encsign=False, # Add '--sign' when encrypting?
+ digest=None, # One of: sha256, sha1, etc; None == use gpg defaults
+ localuser=None, # Value passed to --local-user to set default key for signing, etc
+ base64=True, # Add '--armor' when encrypting/signing?
+ symmetric=False, # Add '--symmetric'?
+ passwd=None, # Passphrase for symmetric
+ asymmetric=False, # Add '--encrypt'?
+ recip=None, # Recipients for asymmetric (semicolon-delimited)
+ enctoself=False, # Add first id from secret keyring as recipient?
+ cipher=None, # One of: aes256, 3des, etc; None == use gpg defaults
+ verbose=False, # Add '--verbose'?
+ alwaystrust=False, # Add '--trust-model always'?
+ yes=True # Add '--yes'? (will overwrite files)
+ ):
+ """Build a gpg cmdline and then launch gpg, saving output appropriately.
This method inspects the contents of class attr 'io' -- a dict object that should
- contain all of the following keys, at least initialized to 0 or '':
+ contain all the following keys, at least initialized to 0 or '':
stdin # Input text for subprocess
infile # Input filename for subprocess, in place of stdin
outfile # Output filename if infile was given
- io['infile'] should contain a filename OR be set to 0, in which case io'[stdin']
+ io['infile'] should contain a filename OR be set to 0, in which case io['stdin']
must contain the input data. If using infile, outfile is not necessarily required,
but it's probably a good idea unless you're doing sign-only.
@@ -147,32 +119,32 @@ def gpg(
the caller (i.e., by examining the Popen instance's returncode attribute).
"""
-
+
if self.io['infile'] and self.io['infile'] == self.io['outfile']:
stderr.write("Same file for both input and output, eh? Is it going "
"to work? ... NOPE. Chuck Testa.\n")
raise Exception("infile, outfile must be different")
-
- fd_pwd_R = None
- fd_pwd_W = None
- useagent = True
- cmd = [self.GPG_BINARY]
-
+
+ fd_pwd_R = None
+ fd_pwd_W = None
+ useagent = True
+ cmd = ['gpg']
+
if self.io['gstatus']:
# Status to file descriptor option
cmd.append('--status-fd')
cmd.append(str(self.io['gstatus'][1]))
-
+
# Setup passphrase file descriptor for symmetric enc/dec
if (action in 'enc' and symmetric and passwd and not encsign) or (
- action in 'dec' and symmetric and passwd):
- useagent=False
- fd_pwd_R, fd_pwd_W = pipe()
- write(fd_pwd_W, passwd)
- close(fd_pwd_W)
- cmd.append('--passphrase-fd')
- cmd.append(str(fd_pwd_R))
-
+ action in 'dec' and symmetric and passwd):
+ useagent = False
+ fd_pwd_R, fd_pwd_W = pipe()
+ write(fd_pwd_W, passwd)
+ close(fd_pwd_W)
+ cmd.append('--passphrase-fd')
+ cmd.append(str(fd_pwd_R))
+
# Encrypt opts
if action in 'enc':
if encsign:
@@ -191,48 +163,47 @@ def gpg(
if enctoself:
cmd.append('--recipient')
if localuser:
- cmd.append(localuser)
+ cmd.append("'" + localuser + "'")
else:
- cmd.append(self.get_gpgdefaultkey())
+ cmd.append(self.get_gpg_default_key())
if recip:
while recip[-1] == ' ' or recip[-1] == ';':
recip = recip.strip()
recip = recip.strip(';')
for r in recip.split(';'):
cmd.append('--recipient')
- cmd.append(r)
-
+ cmd.append("'" + r + "'")
+
# Decrypt opts
elif action in 'dec':
cmd.append('--decrypt')
-
+
# Sign opts
elif action in {'embedsign', 'clearsign', 'detachsign'}:
- if action in 'embedsign': cmd.append('--sign')
- elif action in 'clearsign': cmd.append('--clearsign')
- elif action in 'detachsign': cmd.append('--detach-sign')
+ if action in 'embedsign':
+ cmd.append('--sign')
+ elif action in 'clearsign':
+ cmd.append('--clearsign')
+ elif action in 'detachsign':
+ cmd.append('--detach-sign')
if digest:
cmd.append('--digest-algo')
cmd.append(digest)
-
+
# Verify opts
elif action in 'verify':
cmd.append('--verify')
-
+
# Wouldn't hurt to use armor for all, but it only works with these 3
if action in {'enc', 'embedsign', 'detachsign'}:
if base64:
cmd.append('--armor')
-
+
# Action-independent opts
if useagent:
- if self.GPG_BINARY in 'gpg':
- cmd.append('--use-agent')
+ cmd.append('--use-agent')
else:
- if self.GPG_BINARY in 'gpg':
- cmd.append('--no-use-agent')
- else:
- cmd.append('--batch')
+ cmd.append('--no-use-agent')
if localuser:
cmd.append('--local-user')
cmd.append(localuser)
@@ -249,40 +220,41 @@ def gpg(
cmd.append(self.io['outfile'])
if self.io['infile']:
cmd.append(self.io['infile'])
-
+
# Print a separator + the command-arguments to stderr
flatten_list_to_stderr(cmd)
-
- # If working direct with files, setup our Popen instance with no stdin
+
+ # If working direct with files, set up our Popen instance with no stdin
if self.io['infile']:
self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][1])
# Otherwise, only difference for Popen is we need the stdin pipe
else:
- self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=self.io['stderr'][1])
-
+ b = self.io['stderr']
+ self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=b[1])
+
# Time to communicate! Save output for later
- self.io['stdout'] = self.childprocess.communicate(input=self.io['stdin'])[0]
-
+ b = self.io['stdin'].encode('utf-8')
+ self.io['stdout'] = self.childprocess.communicate(input=b)[0]
+
# Clear stdin from our dictionary asap, in case it's huge
self.io['stdin'] = ''
-
+
# Close os file descriptors
- if fd_pwd_R: close(fd_pwd_R)
+ if fd_pwd_R:
+ close(fd_pwd_R)
sleep(0.1) # Sleep a bit to ensure everything gets read
close(self.io['stderr'][1])
if self.io['gstatus']:
close(self.io['gstatus'][1])
-
-
- def get_gpgdefaultkey(self):
- """Return key id of first secret key in gpg keyring."""
- return check_output(split(
- "{} --list-secret-keys --with-colons --fast-list-mode"
- .format(self.GPG_BINARY))).split(':', 5)[4]
+ def get_gpg_default_key(self):
+ """Return key id of first secret key in gpg keyring."""
+ cmd = split("gpg --list-secret-keys --with-colons --fast-list-mode")
+ output = str(check_output(cmd))
+ return output.split(':', 5)[4]
-class Openssl():
+class Openssl:
"""OpenSSL interface for encryption/decryption.
First thing: use subprocess module to call an openssl process, ensuring it
@@ -296,47 +268,46 @@ class Openssl():
passed to openssl via an os file descriptor.
"""
-
+
def __init__(self, show_version=True):
"""Confirm we can run openssl."""
-
+
try:
vers = Popen(['openssl', 'version'], stdout=PIPE).communicate()[0]
except:
stderr.write("OpenSSL not found on your system.\n\n")
raise
-
+
# To show or not to show version info
if show_version:
stderr.write("{}\n".format(vers))
-
+
# I/O dictionary obj
self.io = dict(
- stdin='', # Stores input text for subprocess
+ stdin='', # Stores input text for subprocess
stdout='', # Stores stdout stream from subprocess
- stderr=0, # Stores tuple of r/w file descriptors for stderr stream
- infile=0, # Input filename for subprocess
+ stderr=0, # Stores tuple of r/w file descriptors for stderr stream
+ infile=0, # Input filename for subprocess
outfile=0) # Output filename for subprocess
-
+
self.childprocess = None
-
-
+
# Main openssl interface method
def openssl(
- self,
- action, # One of: enc, dec
- passwd, # Passphrase for symmetric
- base64=True, # Add '-a' when encrypting/decrypting?
- cipher=None, # Cipher in gpg-format; None = use aes256
- ):
+ self,
+ action, # One of: enc, dec
+ passwd, # Passphrase for symmetric
+ base64=True, # Add '-a' when encrypting/decrypting?
+ cipher=None, # Cipher in gpg-format; None = use aes256
+ ):
"""Build an openssl cmdline and then launch it, saving output appropriately.
This method inspects the contents of class attr 'io' -- a dict object that should
- contain all of the following keys, at least initialized to 0 or '':
+ contain all the following keys, at least initialized to 0 or '':
stdin # Input text for subprocess
infile # Input filename for subprocess, in place of stdin
outfile # Output filename -- required if infile was given
- io['infile'] should contain a filename OR be set to 0, in which case io'[stdin']
+ io['infile'] should contain a filename OR be set to 0, in which case io['stdin']
must contain the input data.
Whether reading input from infile or stdin, each openssl command's stdout &
@@ -347,35 +318,46 @@ def openssl(
the caller (i.e., by examining the Popen instance's returncode attribute).
"""
-
+
if self.io['infile'] and self.io['infile'] == self.io['outfile']:
stderr.write("Same file for both input and output, eh? Is it going "
"to work? ... NOPE. Chuck Testa.\n")
raise Exception("infile, outfile must be different")
-
- if cipher: cipher = cipher.lower()
- if cipher == None: cipher = 'aes-256-cbc'
- elif cipher == '3des': cipher = 'des-ede3-cbc'
- elif cipher == 'cast5': cipher = 'cast5-cbc'
- elif cipher == 'blowfish': cipher = 'bf-cbc'
- elif cipher == 'aes': cipher = 'aes-128-cbc'
- elif cipher == 'aes192': cipher = 'aes-192-cbc'
- elif cipher == 'aes256': cipher = 'aes-256-cbc'
- elif cipher == 'camellia128': cipher = 'camellia-128-cbc'
- elif cipher == 'camellia192': cipher = 'camellia-192-cbc'
- elif cipher == 'camellia256': cipher = 'camellia-256-cbc'
- #else: cipher = 'aes-256-cbc'
-
- fd_pwd_R = None
- fd_pwd_W = None
- cmd = ['openssl', cipher, '-md', 'sha256', '-pass']
-
+
+ if cipher:
+ cipher = cipher.lower()
+ if cipher is None:
+ cipher = 'aes-256-cbc'
+ elif cipher == '3des':
+ cipher = 'des-ede3-cbc'
+ elif cipher == 'cast5':
+ cipher = 'cast5-cbc'
+ elif cipher == 'blowfish':
+ cipher = 'bf-cbc'
+ elif cipher == 'aes':
+ cipher = 'aes-128-cbc'
+ elif cipher == 'aes192':
+ cipher = 'aes-192-cbc'
+ elif cipher == 'aes256':
+ cipher = 'aes-256-cbc'
+ elif cipher == 'camellia128':
+ cipher = 'camellia-128-cbc'
+ elif cipher == 'camellia192':
+ cipher = 'camellia-192-cbc'
+ elif cipher == 'camellia256':
+ cipher = 'camellia-256-cbc'
+ # else: cipher = 'aes-256-cbc'
+
+ fd_pwd_R = None
+ fd_pwd_W = None
+ cmd = ['openssl', cipher, '-md', 'sha256', '-pass']
+
# Setup passphrase file descriptors
fd_pwd_R, fd_pwd_W = pipe()
write(fd_pwd_W, passwd)
close(fd_pwd_W)
cmd.append('fd:{}'.format(fd_pwd_R))
-
+
if base64:
cmd.append('-a')
if action in 'enc':
@@ -387,26 +369,25 @@ def openssl(
cmd.append(self.io['infile'])
cmd.append('-out')
cmd.append(self.io['outfile'])
-
+
# Print a separator + the command-arguments to stderr
flatten_list_to_stderr(cmd)
-
- # If working direct with files, setup our Popen instance with no stdin
+
+ # If working direct with files, set up our Popen instance with no stdin
if self.io['infile']:
self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][1])
# Otherwise, only difference for Popen is we need the stdin pipe
else:
self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=self.io['stderr'][1])
-
+
# Time to communicate! Save output for later
- self.io['stdout'] = self.childprocess.communicate(input=self.io['stdin'])[0]
-
+ b = self.io['stdin'].encode('utf-8')
+ self.io['stdout'] = self.childprocess.communicate(input=b)[0]
+
# Clear stdin from our dictionary asap, in case it's huge
self.io['stdin'] = ''
-
+
# Close os file descriptors
close(fd_pwd_R)
sleep(0.1) # Sleep a bit to ensure everything gets read
close(self.io['stderr'][1])
-
-
diff --git a/pyrite/messages.py b/pyrite/messages.py
new file mode 100644
index 0000000..c385378
--- /dev/null
+++ b/pyrite/messages.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python3
+# Copyright 2012, 2013 Ryan Sawhill Aroha
+#
+# License:
+#
+# Pyrite is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Pyrite. If not, see .
+
+SUCCESS = 0
+INFO = 1
+QUESTION = 2
+WARNING = 3
+ERROR = 4
+
+
+def msg(text, type, icon, timeout=5):
+ """Dictionar-ify input arguments."""
+ return {'text': text, 'type': type, 'icon': icon, 'timeout': timeout}
+
+
+# INFOBAR MESSAGES FOR MAIN WINDOW
+MESSAGE_DICT = dict(
+
+ # Backend Engine
+ engine_openssl_missing=msg(
+ ("Your system does not appear to have OpenSSL."),
+ INFO, WARNING),
+
+ engine_gpg_missing=msg(
+ ("GnuPG not found. Operating in OpenSSL fallback-mode.\n"
+ "To make full use of this program you need gpg installed.\n"
+ "Without it, you won't have access key-based functions like\n"
+ "asymmetric encryption or singing."),
+ INFO, WARNING, 20),
+
+ engine_all_missing=msg(
+ ("This program requires one of: gpg or openssl\n"
+ "None of these were found on your system. You can look around\n"
+ "the interface, but to have real fun you'll need to install gpg\n"
+ "from your linux distribution's software repository."),
+ ERROR, WARNING, 0),
+
+ engine_openssl_notice=msg(
+ ("OpenSSL only supports symmetric {{en,de}}cryption.\n"
+ "All key-based functions are disabled."),
+ INFO, INFO, 7),
+
+ # Textview Message Area Operations
+ txtview_empty=msg(
+ ("{customtext}"),
+ INFO, WARNING, 2),
+
+ txtview_fileopen_error=msg(
+ ("Error. Could not open file:\n"
+ "{filename}"),
+ WARNING, ERROR),
+
+ txtview_fileopen_binary_error=msg(
+ ("To operate on binary files, use the\n"
+ "Input File For Direct Operation chooser button."),
+ INFO, WARNING, 8),
+
+ txtview_save_success=msg(
+ ("Saved contents of Message area to file:\n"
+ "{filename}"),
+ INFO, SUCCESS),
+
+ txtview_save_error=msg(
+ ("Error. Could not save to file:\n"
+ "{filename}"),
+ WARNING, ERROR),
+
+ txtview_copyall_success=msg(
+ ("Copied contents of Message area to clipboard."),
+ INFO, SUCCESS, 3),
+
+ # Filemode Operations
+ filemode_fileopen_error=msg(
+ ("Error. Could not open file:\n"
+ "{filename}\n"
+ "Choose a new file."),
+ WARNING, ERROR),
+
+ filemode_blue_banner=msg(
+ ("Encrypt, Decrypt, Sign, or Verify?\n"
+ "Choose an action to perform on file:\n"
+ "{filename}\n"
+ "You will be prompted for an output filename if necessary."),
+ QUESTION, QUESTION, 0),
+
+ # Main xface (Enc/Dec/Sign/Verify) Operations
+ x_missing_passphrase=msg(
+ ("Passphrase?"),
+ INFO, QUESTION, 3),
+
+ x_canceled_filemode=msg(
+ ("{customtext} operation canceled.\n"
+ "To choose different input or output filenames, select Cancel\n"
+ "from the blue bar below."),
+ INFO, WARNING, 6),
+
+ x_canceled_textmode=msg(
+ ("{customtext} operation canceled."),
+ INFO, WARNING, 4),
+
+ x_opensslenc_success_filemode=msg(
+ ("OpenSSL encrypted input file with {customtext} cipher;\n"
+ "saved output to file:\n"
+ "{filename}\n"
+ "In order to decrypt that file in the future, you will need to \n"
+ "remember which cipher you used .. or guess until you figure it out."),
+ INFO, SUCCESS, 10),
+
+ x_opensslenc_success_textmode=msg(
+ ("OpenSSL encrypted input using {customtext} cipher.\n"
+ "In order to decrypt the output in the future, you will need to \n"
+ "remember which cipher you used .. or guess until you figure it out."),
+ INFO, SUCCESS, 9),
+
+ x_crypt_success_filemode=msg(
+ ("Saved {customtext}rypted copy of input to file:\n"
+ "{filename}"),
+ INFO, SUCCESS),
+
+ x_sign_success_filemode=msg(
+ ("Saved signed copy of input to file:\n"
+ "{filename}"),
+ INFO, SUCCESS),
+
+ x_detachsign_success_filemode=msg(
+ ("Saved detached signature of input to file:\n"
+ "{filename}"),
+ INFO, SUCCESS),
+
+ x_verify_success=msg(
+ ("Signature verified. Data integrity intact."),
+ INFO, SUCCESS, 4),
+
+ x_verify_failed=msg(
+ ("Signature or data integrity could not be verified.\n"
+ "See Task Status for details."),
+ WARNING, ERROR, 7),
+
+ x_missing_recip=msg(
+ ("For whom do you want to encrypt your message?\n"
+ "If you don't want to enter recipients and you don't want to select\n"
+ " Enc. To Self, you must add one of the directives\n"
+ "\tdefault-recipient-self\n"
+ "\tdefault-recipient name\n"
+ "to your gpg.conf file."),
+ WARNING, QUESTION, 0),
+
+ x_generic_failed_filemode=msg(
+ ("Problem {customtext}ing file.\n"
+ "See Task Status for details. Try a different passphrase or Cancel."),
+ WARNING, ERROR, 8),
+
+ x_generic_failed_textmode=msg(
+ ("Problem {customtext}ing input.\n"
+ "See Task Status for details."),
+ WARNING, ERROR),
+
+ # OpenSSL Cipher Warnings
+ cipher_openssl_no_default=msg(
+ ("OpenSSL has no default cipher.\n"
+ "AES256 is a good choice."),
+ INFO, INFO, 7),
+
+ cipher_openssl_no_twofish=msg(
+ ("OpenSSL has no support for the Twofish cipher."),
+ INFO, INFO),
+
+ cipher_openssl_aes_note=msg(
+ ("Note for the command-line geeks:\n"
+ "AES translates to OpenSSL's aes-128-cbc."),
+ INFO, INFO),
+
+ # Preferences Actions
+ preferences_save_success=msg(
+ ("Saved preferences to {filename}\n"
+ "but no changes made to current session."),
+ INFO, SUCCESS),
+
+ preferences_apply_success=msg(
+ ("Saved preferences to {filename}\n"
+ "and applied them to current session."),
+ INFO, SUCCESS),
+
+)
+
+# INFOBAR MESSAGES FOR PREFERENCES DIALOG
+PREFS_MESSAGE_DICT = dict(
+
+ prefs_save_failed=msg(
+ ("Saving preferences failed.\n"
+ "Unable to open config file {filename} for writing."),
+ ERROR, WARNING, 10),
+
+ prefs_reverted=msg(
+ ("Reverted to user-saved preferences."),
+ INFO, SUCCESS, 3),
+
+ prefs_reset_to_defaults=msg(
+ ("Preferences reset to defaults. You still need to Save or Apply."),
+ INFO, SUCCESS, 3),
+
+ prefs_notice_enctoself=msg(
+ ("If you want Encrypt to Self on in Symmetric mode, you must set\n"
+ "Encryption Type to 'Both'."),
+ INFO, INFO),
+
+ prefs_notice_addsig=msg(
+ ("If you want Add Signature on in Symmetric mode, you must also enable\n"
+ "Advanced."),
+ INFO, INFO),
+
+ prefs_notice_enc_both=msg(
+ ("In order for both encryption types to be on by default, Advanced will also be\n"
+ "turned on, whether or not you select it now."),
+ INFO, INFO),
+
+)
diff --git a/modules/prefs.py b/pyrite/prefs.py
similarity index 51%
rename from modules/prefs.py
rename to pyrite/prefs.py
index 4074209..8664dca 100644
--- a/modules/prefs.py
+++ b/pyrite/prefs.py
@@ -1,9 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# This file is part of Pyrite.
-# Last file mod: 2013/09/15
-# Latest version at
+#!/usr/bin/env python3
# Copyright 2012, 2013 Ryan Sawhill Aroha
#
# License:
@@ -20,19 +15,23 @@
#
# You should have received a copy of the GNU General Public License
# along with Pyrite. If not, see .
-#
-#------------------------------------------------------------------------------
# StdLib:
-import gtk
-import glib
-import cPickle as pickle
+import gi
+
+gi.require_version('GLib', '2.0')
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import GLib
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository import GObject
+import pickle as pickle
from sys import stderr
from os import access, R_OK
# Custom Modules:
-import cfg
-from messages import PREFS_MESSAGE_DICT as MESSAGE_DICT
-
+from . import cfg
+from .messages import PREFS_MESSAGE_DICT as MESSAGE_DICT
class Preferences:
@@ -41,10 +40,9 @@ class Preferences:
Try to read preferences from user preferences file; failing that, initialize
good defaults. This class also includes the Preferences-setting window.
"""
-
-
+
def __init__(self, reset_defaults=False):
-
+
try:
if reset_defaults:
raise Exception
@@ -54,7 +52,7 @@ def __init__(self, reset_defaults=False):
raise Exception
self.p = dict(pickle.load(f))
stderr.write("Pyrite loaded preferences from file {!r}\n".format(cfg.USERPREF_FILE))
-
+
except:
stderr.write("Pyrite loaded default preferences\n")
# Default preferences
@@ -83,177 +81,171 @@ def __init__(self, reset_defaults=False):
taskstatus=True,
verbose=False,
wrap=True,
- opc_slider=False,
- opacity=100,
msgfntsize=9,
errfntsize=7,
color_fg='#000000000000',
color_bg='#ffffffffffff')
-
-
+
def infobar(self, id, filename=None, customtext=None):
"""Popup a new auto-hiding InfoBar."""
-
+
# CB for destroy timeout
def destroy_ibar():
self.ibar_timeout = 0
self.ibar.destroy()
- self.window.resize(1,1)
-
+ self.window.resize(1, 1)
+
# If infobar already active: delete old timeout, destroy old ibar
if self.ibar_timeout > 0:
- glib.source_remove(self.ibar_timeout)
+ GObject.source_remove(self.ibar_timeout)
destroy_ibar()
-
+
# Find the needed dictionary inside our message dict, by id
MSG = MESSAGE_DICT[id]
- # Use value from MSG type & icon to lookup Gtk constant, e.g. gtk.MESSAGE_INFO
- msgtype = cfg.MSGTYPES[ MSG['type'] ]
- imgtype = cfg.IMGTYPES[ MSG['icon'] ]
+ # Use value from MSG type & icon to lookup Gtk constant, e.g. Gtk.MessageType.INFO
+ msgtype = cfg.MSGTYPES[MSG['type']]
+ imgtype = cfg.IMGTYPES[MSG['icon']]
# Replace variables in message text & change text color
message = ("" +
MSG['text'].format(filename=filename, customtext=customtext) +
"")
-
+
# Now that we have all the data we need, START creating!
- self.ibar = gtk.InfoBar()
+ self.ibar = Gtk.InfoBar()
self.ibar.set_message_type(msgtype)
- self.vbox_ib.pack_end (self.ibar, False, False)
- img = gtk.Image()
- img.set_from_stock (imgtype, gtk.ICON_SIZE_LARGE_TOOLBAR)
- label = gtk.Label()
- label.set_markup (message)
- content = self.ibar.get_content_area()
- content.pack_start (img, False, False)
- content.pack_start (label, False, False)
- img.show ()
- label.show ()
- self.ibar.show ()
- self.ibar_timeout = glib.timeout_add_seconds(MSG['timeout'], destroy_ibar)
-
-
+ self.vbox_ib.pack_end(self.ibar, False, False, 0)
+ img = Gtk.Image()
+ img.set_from_stock(imgtype, Gtk.IconSize.LARGE_TOOLBAR)
+ label = Gtk.Label()
+ label.set_markup(message)
+ content = self.ibar.get_content_area()
+ content.pack_start(img, False, False, 0)
+ content.pack_start(label, False, False, 0)
+ img.show()
+ label.show()
+ self.ibar.show()
+ self.ibar_timeout = GLib.timeout_add_seconds(MSG['timeout'], destroy_ibar)
+
def open_preferences_window(self, parentwindow):
"""Show the preferences window. Duh."""
self.ibar_timeout = 0
- builder = gtk.Builder()
+ builder = Gtk.Builder()
builder.add_from_file(cfg.ASSETDIR + 'ui/preferences.glade')
# Main window
- self.window = builder.get_object('window1')
- self.btn_save = builder.get_object('btn_save')
- self.btn_apply = builder.get_object('btn_apply')
- self.vbox_ib = builder.get_object('vbox_ib')
+ self.window = builder.get_object('window1')
+ self.btn_save = builder.get_object('btn_save')
+ self.btn_apply = builder.get_object('btn_apply')
+ self.vbox_ib = builder.get_object('vbox_ib')
# Main Operation Mode
- self.cb_opmode = builder.get_object('cb_opmode')
+ self.cb_opmode = builder.get_object('cb_opmode')
# Engine
- self.cb_backend = builder.get_object('cb_backend')
+ self.cb_backend = builder.get_object('cb_backend')
# Enc/Dec Mode
- self.cb_enctype = builder.get_object('cb_enctype')
- self.tg_advanced = builder.get_object('tg_advanced')
- self.tg_enctoself = builder.get_object('tg_enctoself')
- self.cb_cipher = builder.get_object('cb_cipher')
- self.tg_addsig = builder.get_object('tg_addsig')
+ self.cb_enctype = builder.get_object('cb_enctype')
+ self.tg_advanced = builder.get_object('tg_advanced')
+ self.tg_enctoself = builder.get_object('tg_enctoself')
+ self.cb_cipher = builder.get_object('cb_cipher')
+ self.tg_addsig = builder.get_object('tg_addsig')
# Mode-Independent
- self.cb_digest = builder.get_object('cb_digest')
- self.tg_defkey = builder.get_object('tg_defkey')
- self.ent_defkey = builder.get_object('ent_defkey')
- self.cb_txtoutput = builder.get_object('cb_txtoutput')
- self.tg_expander = builder.get_object('tg_expander')
+ self.cb_digest = builder.get_object('cb_digest')
+ self.tg_defkey = builder.get_object('tg_defkey')
+ self.ent_defkey = builder.get_object('ent_defkey')
+ self.cb_txtoutput = builder.get_object('cb_txtoutput')
+ self.tg_expander = builder.get_object('tg_expander')
# Sign/Verify Mode
- self.tg_svoutfiles = builder.get_object('tg_svoutfiles')
- self.cb_text_sigmode= builder.get_object('cb_text_sigmode')
- self.cb_file_sigmode= builder.get_object('cb_file_sigmode')
+ self.tg_svoutfiles = builder.get_object('tg_svoutfiles')
+ self.cb_text_sigmode = builder.get_object('cb_text_sigmode')
+ self.cb_file_sigmode = builder.get_object('cb_file_sigmode')
# Display
- self.tg_taskstatus = builder.get_object('tg_taskstatus')
- self.tg_verbose = builder.get_object('tg_verbose')
- self.tg_wrap = builder.get_object('tg_wrap')
- self.tg_opc_slider = builder.get_object('tg_opc_slider')
- self.sp_opacity = builder.get_object('sp_opacity')
- self.sp_msgfntsize = builder.get_object('sp_msgfntsize')
- self.sp_errfntsize = builder.get_object('sp_errfntsize')
- self.btn_color_fg = builder.get_object('btn_color_fg')
- self.btn_color_bg = builder.get_object('btn_color_bg')
+ self.tg_taskstatus = builder.get_object('tg_taskstatus')
+ self.tg_verbose = builder.get_object('tg_verbose')
+ self.tg_wrap = builder.get_object('tg_wrap')
+ self.sp_msgfntsize = builder.get_object('sp_msgfntsize')
+ self.sp_errfntsize = builder.get_object('sp_errfntsize')
+ self.btn_color_fg = builder.get_object('btn_color_fg')
+ self.btn_color_bg = builder.get_object('btn_color_bg')
# TODO: Advanced tab
- #self.tg_args_gpg_e = builder.get_object('tg_args_gpg_e')
- #self.en_args_gpg_e = builder.get_object('en_args_gpg_e')
- self.window.set_transient_for(parentwindow)
+ # self.tg_args_gpg_e = builder.get_object('tg_args_gpg_e')
+ # self.en_args_gpg_e = builder.get_object('en_args_gpg_e')
+ # self.set_transient_for(parentwindow)
if access(cfg.USERPREF_FILE, R_OK):
btn_revert = builder.get_object('btn_revert')
btn_revert.set_sensitive(True)
self.populate_pref_window_prefs()
builder.connect_signals(self)
self.window.show()
-
-
+
def populate_pref_window_prefs(self):
"""Set state of widgets in prefs window via preferences."""
# Main Operation Mode
- self.cb_opmode.set_active (self.p['opmode'])
+ self.cb_opmode.set_active(self.p['opmode'])
# Engine
- self.cb_backend.set_active (self.p['backend'])
+ self.cb_backend.set_active(self.p['backend'])
# Enc/Dec Mode
- self.cb_enctype.set_active (self.p['enctype'])
- self.tg_advanced.set_active (self.p['advanced'])
- self.tg_enctoself.set_active (self.p['enctoself'])
- self.cb_cipher.set_active (self.p['cipher'])
- self.tg_addsig.set_active (self.p['addsig'])
+ self.cb_enctype.set_active(self.p['enctype'])
+ self.tg_advanced.set_active(self.p['advanced'])
+ self.tg_enctoself.set_active(self.p['enctoself'])
+ self.cb_cipher.set_active(self.p['cipher'])
+ self.tg_addsig.set_active(self.p['addsig'])
# Mode-Independent
- self.cb_digest.set_active (self.p['digest'])
- self.tg_defkey.set_active (self.p['defkey'])
- self.ent_defkey.set_text (self.p['defkeytxt'])
- self.cb_txtoutput.set_active (self.p['txtoutput'])
- self.tg_expander.set_active (self.p['expander'])
+ self.cb_digest.set_active(self.p['digest'])
+ self.tg_defkey.set_active(self.p['defkey'])
+ self.ent_defkey.set_text(self.p['defkeytxt'])
+ self.cb_txtoutput.set_active(self.p['txtoutput'])
+ self.tg_expander.set_active(self.p['expander'])
# Sign/Verify Mode
- self.tg_svoutfiles.set_active (self.p['svoutfiles'])
- self.cb_text_sigmode.set_active (self.p['text_sigmode'])
- self.cb_file_sigmode.set_active (self.p['file_sigmode'])
+ self.tg_svoutfiles.set_active(self.p['svoutfiles'])
+ self.cb_text_sigmode.set_active(self.p['text_sigmode'])
+ self.cb_file_sigmode.set_active(self.p['file_sigmode'])
# Display
- self.tg_taskstatus.set_active (self.p['taskstatus'])
- self.tg_verbose.set_active (self.p['verbose'])
- self.tg_wrap.set_active (self.p['wrap'])
- self.tg_opc_slider.set_active (self.p['opc_slider'])
- self.sp_opacity.set_value (self.p['opacity'])
- self.sp_msgfntsize.set_value (self.p['msgfntsize'])
- self.sp_errfntsize.set_value (self.p['errfntsize'])
- self.btn_color_fg.set_color (gtk.gdk.color_parse(self.p['color_fg']))
- self.btn_color_bg.set_color (gtk.gdk.color_parse(self.p['color_bg']))
-
-
+ self.tg_taskstatus.set_active(self.p['taskstatus'])
+ self.tg_verbose.set_active(self.p['verbose'])
+ self.tg_wrap.set_active(self.p['wrap'])
+ self.sp_msgfntsize.set_value(self.p['msgfntsize'])
+ self.sp_errfntsize.set_value(self.p['errfntsize'])
+
+ fg_color = Gdk.Color(0, 0, 0)
+ fg_color.parse(self.p['color_fg'])
+
+ bg_color = Gdk.Color(0, 0, 0)
+ bg_color.parse(self.p['color_bg'])
+
+ self.btn_color_fg.set_color(fg_color)
+ self.btn_color_bg.set_color(bg_color)
+
def capture_current_prefs(self):
"""Capture current state of widgets in prefs window & save as preferences."""
self.p = {
# Main Operation Mode
- 'opmode' : self.cb_opmode.get_active(),
+ 'opmode': self.cb_opmode.get_active(),
# Engine
- 'backend' : self.cb_backend.get_active(),
+ 'backend': self.cb_backend.get_active(),
# Enc/Dec Mode
- 'enctype' : self.cb_enctype.get_active(),
- 'advanced' : self.tg_advanced.get_active(),
- 'enctoself' : self.tg_enctoself.get_active(),
- 'cipher' : self.cb_cipher.get_active(),
- 'addsig' : self.tg_addsig.get_active(),
+ 'enctype': self.cb_enctype.get_active(),
+ 'advanced': self.tg_advanced.get_active(),
+ 'enctoself': self.tg_enctoself.get_active(),
+ 'cipher': self.cb_cipher.get_active(),
+ 'addsig': self.tg_addsig.get_active(),
# Mode-Independent
- 'digest' : self.cb_digest.get_active(),
- 'defkey' : self.tg_defkey.get_active(),
- 'defkeytxt' : self.ent_defkey.get_text(),
- 'txtoutput' : self.cb_txtoutput.get_active(),
- 'expander' : self.tg_expander.get_active(),
+ 'digest': self.cb_digest.get_active(),
+ 'defkey': self.tg_defkey.get_active(),
+ 'defkeytxt': self.ent_defkey.get_text(),
+ 'txtoutput': self.cb_txtoutput.get_active(),
+ 'expander': self.tg_expander.get_active(),
# Sign/Verify Mode
- 'svoutfiles' : self.tg_svoutfiles.get_active(),
+ 'svoutfiles': self.tg_svoutfiles.get_active(),
'text_sigmode': self.cb_text_sigmode.get_active(),
'file_sigmode': self.cb_file_sigmode.get_active(),
# Display
- 'taskstatus' : self.tg_taskstatus.get_active(),
- 'verbose' : self.tg_verbose.get_active(),
- 'wrap' : self.tg_wrap.get_active(),
- 'opc_slider' : self.tg_opc_slider.get_active(),
- 'opacity' : self.sp_opacity.get_value(),
- 'msgfntsize' : self.sp_msgfntsize.get_value(),
- 'errfntsize' : self.sp_errfntsize.get_value(),
- 'color_fg' : self.btn_color_fg.get_color().to_string(),
- 'color_bg' : self.btn_color_bg.get_color().to_string()}
+ 'taskstatus': self.tg_taskstatus.get_active(),
+ 'verbose': self.tg_verbose.get_active(),
+ 'wrap': self.tg_wrap.get_active(),
+ 'msgfntsize': self.sp_msgfntsize.get_value(),
+ 'errfntsize': self.sp_errfntsize.get_value(),
+ 'color_fg': self.btn_color_fg.get_color().to_string(),
+ 'color_bg': self.btn_color_bg.get_color().to_string()}
return self.p
-
-
+
# Called by Save button
def save_prefs(self):
"""Attempt to save user prefs to homedir prefs file."""
@@ -266,44 +258,37 @@ def save_prefs(self):
self.infobar('prefs_save_failed', cfg.USERPREF_FILE)
return False
return True
-
-
+
# Called by Cancel button
def action_cancel_prefs(self, w):
"""Close prefs window without doing anything."""
self.window.destroy()
-
-
+
# Called by Revert button
def action_revert_prefs(self, w):
"""Reset state of widgets in prefs window via external preferences file, if avail."""
self.__init__()
self.populate_pref_window_prefs()
self.infobar('prefs_reverted')
-
-
+
# Called by Defaults button
def action_default_prefs(self, w):
"""Reset state of widgets in prefs window to predefined defaults."""
self.__init__(reset_defaults=True)
self.populate_pref_window_prefs()
self.infobar('prefs_reset_to_defaults')
-
-
+
def action_tg_enctoself(self, w):
"""Show some info when user enables enctoself toggle."""
if w.get_active():
self.infobar('prefs_notice_enctoself')
-
-
+
def action_tg_addsig(self, w):
"""Show some info when user enables addsig toggle."""
if w.get_active():
self.infobar('prefs_notice_addsig')
-
-
+
def action_cb_enctype(self, w):
"""Show some info when user chooses 'Both' in enctype combobox."""
if w.get_active() == 2:
self.infobar('prefs_notice_enc_both')
-
diff --git a/ui/about.glade b/ui/about.glade
index e62dae1..f240220 100644
--- a/ui/about.glade
+++ b/ui/about.glade
@@ -1,17 +1,17 @@
-
-
+