From 8453ce7275dc9aa2303963d9782eae41e0ddb0aa Mon Sep 17 00:00:00 2001 From: Felipe Martin Date: Fri, 11 Oct 2019 22:58:20 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 + .pre-commit-config.yaml | 22 ++ .readme/stopityou.png | Bin 0 -> 30342 bytes LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++ Makefile | 3 + README.md | 49 ++- poetry.lock | 545 ++++++++++++++++++++++++++++++++ pyproject.toml | 22 ++ qute_1pass.py | 302 ++++++++++++++++++ setup.cfg | 16 + 10 files changed, 1633 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .readme/stopityou.png create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100755 qute_1pass.py create mode 100644 setup.cfg diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c1d6aa5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv +.vscode diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5586db7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.2.3 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: flake8 + +- repo: https://github.com/asottile/seed-isort-config + rev: v1.9.2 + hooks: + - id: seed-isort-config +- repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.20 + hooks: + - id: isort + +- repo: https://github.com/ambv/black + rev: stable + hooks: + - id: black + language_version: python3.7 diff --git a/.readme/stopityou.png b/.readme/stopityou.png new file mode 100644 index 0000000000000000000000000000000000000000..954eb7baee58113ca3d9782143b1011ebe1d6fae GIT binary patch literal 30342 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 + 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. + + Qute 1Password + Copyright (C) 2019 Felipe Martínz + + 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: + + Qute 1Password Copyright (C) 2019 Felipe Martín + 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..4152d10 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +setup: + poetry install + poetry run pre-commit install diff --git a/README.md b/README.md index 62acf05..5f509f2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,48 @@ -# qute-1password +# Qute-1Password -Qutebrowser userscript to fill 1password credentials in it \ No newline at end of file +Qutebrowser userscript to fill 1password credentials + +![Oh, stop it! you...](.readme/stopityou.png) + +## Dependencies + +- [The 1Password CLI](https://support.1password.com/command-line-getting-started/) + Ensure you have it installed and set up. Follow the official documentation. +- [rofi](https://github.com/davatorium/rofi) to ask for password and list items + +## Usage + +Right now it defaults to the `my` account, will be configurable in the future. + +``` +./qute_1pass.py --help +usage: qute_1pass.py [-h] [--auto-submit] [--cache-session] command + +positional arguments: + command fill_credentials, fill_totp + +optional arguments: + -h, --help show this help message and exit + --auto-submit Auto submit after filling + --cache-session Cache 1password session for 30 minutes +``` + +Call your script from qutebrowser using + +``` +:spawn --userscript path/to/qute_1pass.py fill_credentials +``` + +## Contributing + +In this project we use Poetry_ to manage the python dependencies and virtual environments. Make sure you have it installed before continuing. + +Use this command to create the virtualenv, install dev-dependencies and +install the pre-commit hook. + +``` bash +make setup +``` + +After you make your desired changes, open a merge request and I will review it as soon +as I can. diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..2030f36 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,545 @@ +[[package]] +category = "dev" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "appdirs" +optional = false +python-versions = "*" +version = "1.4.3" + +[[package]] +category = "dev" +description = "Disable App Nap on OS X 10.9" +marker = "python_version >= \"3.4\" and sys_platform == \"darwin\"" +name = "appnope" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "dev" +description = "A few extensions to pyyaml." +name = "aspy.yaml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.0" + +[package.dependencies] +pyyaml = "*" + +[[package]] +category = "dev" +description = "An abstract syntax tree for Python with inference support." +name = "astroid" +optional = false +python-versions = ">=3.5.*" +version = "2.3.1" + +[package.dependencies] +lazy-object-proxy = ">=1.4.0,<1.5.0" +six = "1.12" +wrapt = ">=1.11.0,<1.12.0" + +[package.dependencies.typed-ast] +python = "<3.8" +version = ">=1.4.0,<1.5" + +[[package]] +category = "dev" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "19.2.0" + +[[package]] +category = "dev" +description = "Specifications for callback functions passed in to an API" +marker = "python_version >= \"3.4\"" +name = "backcall" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "dev" +description = "The uncompromising code formatter." +name = "black" +optional = false +python-versions = ">=3.6" +version = "18.9b0" + +[package.dependencies] +appdirs = "*" +attrs = ">=17.4.0" +click = ">=6.5" +toml = ">=0.9.4" + +[[package]] +category = "dev" +description = "Validate configuration and produce human readable error messages." +name = "cfgv" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.0.1" + +[package.dependencies] +six = "*" + +[[package]] +category = "dev" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "7.0" + +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "python_version >= \"3.4\" and sys_platform == \"win32\" or sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.4.1" + +[[package]] +category = "dev" +description = "Better living through Python with decorators" +marker = "python_version >= \"3.4\"" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.4.0" + +[[package]] +category = "dev" +description = "Discover and load entry points from installed packages." +name = "entrypoints" +optional = false +python-versions = ">=2.7" +version = "0.3" + +[[package]] +category = "dev" +description = "the modular source code checker: pep8, pyflakes and co" +name = "flake8" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.7.8" + +[package.dependencies] +entrypoints = ">=0.3.0,<0.4.0" +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.5.0,<2.6.0" +pyflakes = ">=2.1.0,<2.2.0" + +[[package]] +category = "dev" +description = "File identification library for Python" +name = "identify" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.7" + +[[package]] +category = "dev" +description = "Read metadata from Python packages" +name = "importlib-metadata" +optional = false +python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" +version = "0.23" + +[package.dependencies] +zipp = ">=0.5" + +[[package]] +category = "dev" +description = "IPython-enabled pdb" +name = "ipdb" +optional = false +python-versions = ">=2.7" +version = "0.12.2" + +[package.dependencies] +setuptools = "*" + +[package.dependencies.ipython] +python = ">=3.4" +version = ">=5.1.0" + +[[package]] +category = "dev" +description = "IPython: Productive Interactive Computing" +marker = "python_version >= \"3.4\"" +name = "ipython" +optional = false +python-versions = ">=3.5" +version = "7.8.0" + +[package.dependencies] +appnope = "*" +backcall = "*" +colorama = "*" +decorator = "*" +jedi = ">=0.10" +pexpect = "*" +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<2.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[[package]] +category = "dev" +description = "Vestigial utilities from IPython" +marker = "python_version >= \"3.4\"" +name = "ipython-genutils" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "dev" +description = "A Python utility / library to sort Python imports." +name = "isort" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "4.3.21" + +[[package]] +category = "dev" +description = "An autocompletion tool for Python that can be used for text editors." +marker = "python_version >= \"3.4\"" +name = "jedi" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.15.1" + +[package.dependencies] +parso = ">=0.5.0" + +[[package]] +category = "dev" +description = "A fast and thorough lazy object proxy." +name = "lazy-object-proxy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.2" + +[[package]] +category = "dev" +description = "McCabe checker, plugin for flake8" +name = "mccabe" +optional = false +python-versions = "*" +version = "0.6.1" + +[[package]] +category = "dev" +description = "More routines for operating on iterables, beyond itertools" +name = "more-itertools" +optional = false +python-versions = ">=3.4" +version = "7.2.0" + +[[package]] +category = "dev" +description = "Optional static typing for Python" +name = "mypy" +optional = false +python-versions = ">=3.5" +version = "0.730" + +[package.dependencies] +mypy-extensions = ">=0.4.0,<0.5.0" +typed-ast = ">=1.4.0,<1.5.0" +typing-extensions = ">=3.7.4" + +[[package]] +category = "dev" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +name = "mypy-extensions" +optional = false +python-versions = "*" +version = "0.4.2" + +[[package]] +category = "dev" +description = "Node.js virtual environment builder" +name = "nodeenv" +optional = false +python-versions = "*" +version = "1.3.3" + +[[package]] +category = "dev" +description = "A Python Parser" +marker = "python_version >= \"3.4\"" +name = "parso" +optional = false +python-versions = "*" +version = "0.5.1" + +[[package]] +category = "dev" +description = "Pexpect allows easy control of interactive console applications." +marker = "python_version >= \"3.4\" and sys_platform != \"win32\"" +name = "pexpect" +optional = false +python-versions = "*" +version = "4.7.0" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +category = "dev" +description = "Tiny 'shelve'-like database with concurrency support" +marker = "python_version >= \"3.4\"" +name = "pickleshare" +optional = false +python-versions = "*" +version = "0.7.5" + +[[package]] +category = "dev" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +name = "pre-commit" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.18.3" + +[package.dependencies] +"aspy.yaml" = "*" +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = "*" +nodeenv = ">=0.11.1" +pyyaml = "*" +six = "*" +toml = "*" +virtualenv = ">=15.2" + +[[package]] +category = "dev" +description = "Library for building powerful interactive command lines in Python" +marker = "python_version >= \"3.4\"" +name = "prompt-toolkit" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.0.10" + +[package.dependencies] +six = ">=1.9.0" +wcwidth = "*" + +[[package]] +category = "dev" +description = "Run a subprocess in a pseudo terminal" +marker = "python_version >= \"3.4\" and sys_platform != \"win32\"" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + +[[package]] +category = "dev" +description = "Python style guide checker" +name = "pycodestyle" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.5.0" + +[[package]] +category = "dev" +description = "passive checker of Python programs" +name = "pyflakes" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.1.1" + +[[package]] +category = "dev" +description = "Pygments is a syntax highlighting package written in Python." +marker = "python_version >= \"3.4\"" +name = "pygments" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.4.2" + +[[package]] +category = "dev" +description = "python code static checker" +name = "pylint" +optional = false +python-versions = ">=3.5.*" +version = "2.4.2" + +[package.dependencies] +astroid = ">=2.3.0,<2.4" +colorama = "*" +isort = ">=4.2.5,<5" +mccabe = ">=0.6,<0.7" + +[[package]] +category = "dev" +description = "YAML parser and emitter for Python" +name = "pyyaml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "5.1.2" + +[[package]] +category = "dev" +description = "a python refactoring library..." +name = "rope" +optional = false +python-versions = "*" +version = "0.14.0" + +[[package]] +category = "dev" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "1.12.0" + +[[package]] +category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.0" + +[[package]] +category = "dev" +description = "Traitlets Python config system" +marker = "python_version >= \"3.4\"" +name = "traitlets" +optional = false +python-versions = "*" +version = "4.3.3" + +[package.dependencies] +decorator = "*" +ipython-genutils = "*" +six = "*" + +[[package]] +category = "dev" +description = "a fork of Python 2 and 3 ast modules with type comment support" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.4.0" + +[[package]] +category = "dev" +description = "Type Hints for Python" +name = "typing" +optional = false +python-versions = "*" +version = "3.7.4.1" + +[[package]] +category = "dev" +description = "Backported and Experimental Type Hints for Python 3.5+" +name = "typing-extensions" +optional = false +python-versions = "*" +version = "3.7.4" + +[package.dependencies] +typing = ">=3.7.4" + +[[package]] +category = "dev" +description = "Virtual Python Environment builder" +name = "virtualenv" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "16.7.5" + +[[package]] +category = "dev" +description = "Measures number of Terminal column cells of wide-character codes" +marker = "python_version >= \"3.4\"" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.1.7" + +[[package]] +category = "dev" +description = "Module for decorators, wrappers and monkey patching." +name = "wrapt" +optional = false +python-versions = "*" +version = "1.11.2" + +[[package]] +category = "dev" +description = "Backport of pathlib-compatible object wrapper for zip files" +name = "zipp" +optional = false +python-versions = ">=2.7" +version = "0.6.0" + +[package.dependencies] +more-itertools = "*" + +[metadata] +content-hash = "01c7eadad4114a1808fdcfc879d388836364f0c89062d746fc18d1a7ae9872b9" +python-versions = "^3.7" + +[metadata.hashes] +appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] +appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] +"aspy.yaml" = ["463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc", "e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"] +astroid = ["98c665ad84d10b18318c5ab7c3d203fe11714cbad2a4aef4f44651f415392754", "b7546ffdedbf7abcfbff93cd1de9e9980b1ef744852689decc5aeada324238c6"] +attrs = ["ec20e7a4825331c1b5ebf261d111e16fa9612c1f7a5e1f884f12bd53a664dfd2", "f913492e1663d3c36f502e5e9ba6cd13cf19d7fab50aa13239e420fef95e1396"] +backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] +black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"] +cfgv = ["edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144", "fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"] +click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] +colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] +decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"] +entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] +flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"] +identify = ["4f1fe9a59df4e80fcb0213086fcf502bc1765a01ea4fe8be48da3b65afd2a017", "d8919589bd2a5f99c66302fec0ef9027b12ae150b0b0213999ad3f695fc7296e"] +importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"] +ipdb = ["473fdd798a099765f093231a8b1fabfa95b0b682fce12de0c74b61a4b4d8ee57"] +ipython = ["c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b", "dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1"] +ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] +isort = ["54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", "6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"] +jedi = ["786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", "ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"] +lazy-object-proxy = ["02b260c8deb80db09325b99edf62ae344ce9bc64d68b7a634410b8e9a568edbf", "18f9c401083a4ba6e162355873f906315332ea7035803d0fd8166051e3d402e3", "1f2c6209a8917c525c1e2b55a716135ca4658a3042b5122d4e3413a4030c26ce", "2f06d97f0ca0f414f6b707c974aaf8829c2292c1c497642f63824119d770226f", "616c94f8176808f4018b39f9638080ed86f96b55370b5a9463b2ee5c926f6c5f", "63b91e30ef47ef68a30f0c3c278fbfe9822319c15f34b7538a829515b84ca2a0", "77b454f03860b844f758c5d5c6e5f18d27de899a3db367f4af06bec2e6013a8e", "83fe27ba321e4cfac466178606147d3c0aa18e8087507caec78ed5a966a64905", "84742532d39f72df959d237912344d8a1764c2d03fe58beba96a87bfa11a76d8", "874ebf3caaf55a020aeb08acead813baf5a305927a71ce88c9377970fe7ad3c2", "9f5caf2c7436d44f3cec97c2fa7791f8a675170badbfa86e1992ca1b84c37009", "a0c8758d01fcdfe7ae8e4b4017b13552efa7f1197dd7358dc9da0576f9d0328a", "a4def978d9d28cda2d960c279318d46b327632686d82b4917516c36d4c274512", "ad4f4be843dace866af5fc142509e9b9817ca0c59342fdb176ab6ad552c927f5", "ae33dd198f772f714420c5ab698ff05ff900150486c648d29951e9c70694338e", "b4a2b782b8a8c5522ad35c93e04d60e2ba7f7dcb9271ec8e8c3e08239be6c7b4", "c462eb33f6abca3b34cdedbe84d761f31a60b814e173b98ede3c81bb48967c4f", "fd135b8d35dfdcdb984828c84d695937e58cc5f49e1c854eb311c4d6aa03f4f1"] +mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] +more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"] +mypy = ["1d98fd818ad3128a5408148c9e4a5edce6ed6b58cc314283e631dd5d9216527b", "22ee018e8fc212fe601aba65d3699689dd29a26410ef0d2cc1943de7bec7e3ac", "3a24f80776edc706ec8d05329e854d5b9e464cd332e25cde10c8da2da0a0db6c", "42a78944e80770f21609f504ca6c8173f7768043205b5ac51c9144e057dcf879", "4b2b20106973548975f0c0b1112eceb4d77ed0cafe0a231a1318f3b3a22fc795", "591a9625b4d285f3ba69f541c84c0ad9e7bffa7794da3fa0585ef13cf95cb021", "5b4b70da3d8bae73b908a90bb2c387b977e59d484d22c604a2131f6f4397c1a3", "84edda1ffeda0941b2ab38ecf49302326df79947fa33d98cdcfbf8ca9cf0bb23", "b2b83d29babd61b876ae375786960a5374bba0e4aba3c293328ca6ca5dc448dd", "cc4502f84c37223a1a5ab700649b5ab1b5e4d2bf2d426907161f20672a21930b", "e29e24dd6e7f39f200a5bb55dcaa645d38a397dd5a6674f6042ef02df5795046"] +mypy-extensions = ["a161e3b917053de87dbe469987e173e49fb454eca10ef28b48b384538cc11458"] +nodeenv = ["ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"] +parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"] +pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] +pickleshare = ["87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", "9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"] +pre-commit = ["1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f", "fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"] +prompt-toolkit = ["46642344ce457641f28fc9d1c9ca939b63dadf8df128b86f1b9860e59c73a5e4", "e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31", "f15af68f66e664eaa559d4ac8a928111eebd5feda0c11738b5998045224829db"] +ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] +pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"] +pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"] +pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"] +pylint = ["7edbae11476c2182708063ac387a8f97c760d9cfe36a5ede0ca996f90cf346c8", "844ce067788028c1a35086a5c66bc5e599ddd851841c41d6ee1623b36774d9f2"] +pyyaml = ["0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", "01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", "5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", "5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", "7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", "7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", "87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", "9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", "a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", "b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", "b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", "bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", "f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"] +rope = ["6b728fdc3e98a83446c27a91fc5d56808a004f8beab7a31ab1d7224cecc7d969", "c5c5a6a87f7b1a2095fb311135e2a3d1f194f5ecb96900fdd0a9100881f48aaf", "f0dcf719b63200d492b85535ebe5ea9b29e0d0b8aebeb87fe03fc1a65924fdaf"] +six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] +toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] +traitlets = ["70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44", "d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"] +typed-ast = ["18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"] +typing = ["91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23", "c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36", "f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"] +typing-extensions = ["2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95", "b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87", "d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed"] +virtualenv = ["680af46846662bb38c5504b78bad9ed9e4f3ba2d54f54ba42494fdf94337fe30", "f78d81b62d3147396ac33fc9d77579ddc42cc2a98dd9ea38886f616b33bc7fb2"] +wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] +wrapt = ["565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1"] +zipp = ["3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", "f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..86da66e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[tool.poetry] +name = "qute_1password" +version = "0.1.0" +description = "Userscript to help fill credentials with the 1password CLI" +authors = ["Felipe Martin "] + +[tool.poetry.dependencies] +python = "^3.7" + +[tool.poetry.dev-dependencies] +black = {version = "^18.3-alpha.0", allows-prereleases = true} +flake8 = "^3.7" +isort = "^4.3" +pre-commit = "^1.18" +rope = "^0.14.0" +ipdb = "^0.12.2" +pylint = "^2.4" +mypy = "^0.730.0" + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/qute_1pass.py b/qute_1pass.py new file mode 100755 index 0000000..07a43b1 --- /dev/null +++ b/qute_1pass.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 + +import os +import sys +import json +import logging +import argparse +import tempfile +import subprocess +from datetime import datetime, timedelta +from urllib.parse import urlsplit + +logger = logging.getLogger("qute_1pass") + +CACHE_DIR = os.path.join(tempfile.gettempdir(), "qute_1pass") +os.makedirs(CACHE_DIR, exist_ok=True) + +SESSION_PATH = os.path.join(CACHE_DIR, "session") +SESSION_DURATION = timedelta(minutes=30) + +LAST_ITEM_PATH = os.path.join(CACHE_DIR, "last_item") +LAST_ITEM_DURATION = timedelta(seconds=10) + +OP_SUBDOMAIN = "my" +CMD_PASSWORD_PROMPT = ( + "rofi -password -dmenu -p 'Vault Password' -l 0 -sidebar -width 20" +) +CMD_ITEM_SELECT = "echo -e '{items}' | rofi -dmenu -p 'Select login'" +CMD_LIST_PROMPT = "echo {items} | rofi -dmenu" + +CMD_OP_LOGIN = "echo -n {password} | op signin {subdomain} --output=raw" +CMD_OP_LIST_ITEMS = "op list items --session={session_id}" +CMD_OP_GET_ITEM = "op get item {uuid} --session={session_id}" +CMD_OP_GET_TOTP = "op get totp {uuid} --session={session_id}" + +QUTE_FIFO = os.environ["QUTE_FIFO"] + +parser = argparse.ArgumentParser() +parser.add_argument("command", help="fill_credentials, fill_totp") +parser.add_argument( + "--auto-submit", help="Auto submit after filling", action="store_true" +) +parser.add_argument( + "--cache-session", + help="Cache 1password session for 30 minutes", + action="store_true", +) + + +class Qute: + """Logic related to qutebrowser""" + + @classmethod + def _command(cls, command, *args): + with open(QUTE_FIFO, "w") as fifo: + logger.info(f"{command} {' '.join(args)}") + fifo.write(f"{command} {' '.join(args)}\n") + fifo.flush() + + @classmethod + def _message(cls, message, type="error"): + cls._command(f"message-{type}", f"'qute-1password: {message}'") + + @classmethod + def message_error(cls, message): + cls._message(message) + + @classmethod + def message_warning(cls, message): + cls._message(message, type="warning") + + @classmethod + def fake_key(cls, key): + key = key.replace(" ", "") + cls._command("fake-key", key) + + @classmethod + def fill_credentials_tabmode(cls, username, password, submit=False): + cls.fake_key(username) + cls.fake_key("") + cls.fake_key(password) + if submit: + cls.fake_key("") + + @classmethod + def fill_totp(cls, totp, submit=True): + cls.fake_key(totp) + if submit: + cls.fake_key("") + + +class ExecuteError(Exception): + """Used when commands executed return code is not 0""" + + pass + + +def execute_command(command): + """Executes a command, mainly used to launch commands for user input and the op cli""" + result = subprocess.run(command, shell=True, capture_output=True, encoding="utf-8") + + if result.returncode != 0: + logger.error(result.stderr) + raise ExecuteError(result.stderr) + + return result.stdout.strip() + + +def extract_host(url): + """Extracts the host from a given URL""" + _, host, *_ = urlsplit(url) + return host + + +class OnePass: + """Logic related to the op command and parsing results""" + + @classmethod + def login(cls): + try: + password = execute_command(CMD_PASSWORD_PROMPT) + except ExecuteError: + Qute.message_error("Error calling pinentry program") + sys.exit(1) + + try: + session_id = execute_command( + CMD_OP_LOGIN.format(password=password, subdomain=OP_SUBDOMAIN) + ) + except ExecuteError: + Qute.message_error("Login error") + sys.exit(1) + + if arguments.cache_session: + with open(SESSION_PATH, "w") as handler: + handler.write(session_id) + + return session_id + + @classmethod + def get_session(cls): + """ + Returns a session for the op command to make calls with. + If a session is cached, we check if it's expired first to avoid any errors. + """ + if arguments.cache_session and os.path.isfile(SESSION_PATH): + # op sessions last 30 minutes, check if still valid + creation_time = datetime.fromtimestamp(os.stat(SESSION_PATH).st_ctime) + if (datetime.now() - creation_time) < SESSION_DURATION: + return open(SESSION_PATH, "r").read() + else: + # Session expired + os.unlink(SESSION_PATH) + + return cls.login() + + @classmethod + def list_items(cls): + session_id = cls.get_session() + result = execute_command(CMD_OP_LIST_ITEMS.format(session_id=session_id)) + parsed = json.loads(result) + return parsed + + @classmethod + def get_item(cls, uuid): + session_id = cls.get_session() + try: + result = execute_command( + CMD_OP_GET_ITEM.format(uuid=uuid, session_id=session_id) + ) + except ExecuteError: + logger.error("Error retrieving credential", exc_info=True) + parsed = json.loads(result) + + return parsed + + @classmethod + def get_item_for_url(cls, url): + host = extract_host(url) + + def filter_host(item): + """Exclude items that does not match host and are not a login""" + if item["templateUuid"] != "001": + return False + if "url" in item["overview"]: + return host in item["overview"]["url"] + return False + + items = cls.list_items() + filtered = filter(filter_host, items) + mapping = { + f"{host}: {item['overview']['title']} ({item['uuid']})": item + for item in filtered + } + + if not mapping: + raise cls.NoItemsFoundError(f"No items found for host {host}") + + try: + credential = execute_command( + CMD_ITEM_SELECT.format(items="\n".join(mapping.keys())) + ) + except ExecuteError: + pass + + if not credential: + # Cancelled + return + + return cls.get_item(mapping[credential]["uuid"]) + + @classmethod + def get_credentials(cls, item): + username = password = None + for field in item["details"]["fields"]: + if field.get("designation") == "username": + username = field["value"] + if field.get("designation") == "password": + password = field["value"] + + if username is None or password is None: + logger.warning( + "Present: username={username} password={password}".format( + username=username is not None, password=password is not None + ) + ) + Qute.message_warning("Filled incomplete credentials") + + return username, password + + @classmethod + def get_totp(cls, uuid): + session_id = cls.get_session() + try: + return execute_command( + CMD_OP_GET_TOTP.format(uuid=uuid, session_id=session_id) + ) + except ExecuteError: + logger.error("Error retrieving TOTP", exc_info=True) + + class NoItemsFoundError(Exception): + pass + + +class CLI: + def __init__(self, arguments): + self.arguments = arguments + + def run(self): + command = self.arguments.command + if command != "run" and not command.startswith("_") and hasattr(self, command): + return getattr(self, command)() + + def _get_item(self): + try: + item = OnePass.get_item_for_url(os.environ["QUTE_URL"]) + except OnePass.NoItemsFoundError as error: + Qute.message_warning(error) + sys.exit(0) + return item + + def fill_credentials(self): + item = self._get_item() + username, password = OnePass.get_credentials(item) + Qute.fill_credentials_tabmode( + username, password, submit=self.arguments.auto_submit + ) + # Store a reference to this items in case we need a quick reference to the UUID + # to get the TOTP later + last_item = {"host": extract_host(os.environ["QUTE_URL"]), "uuid": item["uuid"]} + with open(LAST_ITEM_PATH, "w") as handler: + handler.write(json.dumps(last_item)) + + def fill_totp(self): + # Check last item first + # If theres a last_item file created in the last LAST_ITEM_DURATION seconds + # and the host matches the one the user is visiting, use that UUID to retrieve + # the totp + item = None + + if os.path.isfile(LAST_ITEM_PATH): + creation_time = datetime.fromtimestamp(os.stat(LAST_ITEM_PATH).st_ctime) + if (datetime.now() - creation_time) < LAST_ITEM_DURATION: + last_item = json.loads(open(LAST_ITEM_PATH, "r").read()) + if last_item["host"] == extract_host(os.environ["QUTE_URL"]): + item = last_item + + if not item: + item = self._get_item() + + totp = OnePass.get_totp(item["uuid"]) + logger.error(totp) + Qute.fill_totp(totp) + + if os.path.isfile(LAST_ITEM_PATH): + os.unlink(LAST_ITEM_PATH) + + +if __name__ == "__main__": + arguments = parser.parse_args() + cli = CLI(arguments) + sys.exit(cli.run()) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..9455b01 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,16 @@ +[flake8] +ignore = E203, E266, E501, W503, F403 +max-line-length = 88 +max-complexity = 18 +select = B,C,E,F,W,T4,B9 + +[isort] +use_parentheses = True +multi_line_output = 3 +include_trailing_comma = True +length_sort = 1 +lines_between_types = 0 +line_length = 88 +known_third_party = +sections = FUTURE, STDLIB, DJANGO, THIRDPARTY, FIRSTPARTY, LOCALFOLDER +no_lines_before = LOCALFOLDER