././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/0000775000175000017500000000000000000000000014527 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/.quickly0000644000175000017500000000010000000000000016176 0ustar00mdeslaurmdeslaur00000000000000project = pasaffe version = 12.04 template = ubuntu-application ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1455306397.0 pasaffe-0.57/AUTHORS0000644000175000017500000000211200000000000015571 0ustar00mdeslaurmdeslaur00000000000000Copyright (C) 2011-2013 Marc Deslauriers bin/pasaffe-cli: Copyright (C) 2015 C de-Avillez bin/pasaffe-dump-db: Copyright (C) 2011 Jamie Strandboge bin/pasaffe-import-figaroxml: Copyright (C) 2011 Marc Deslauriers Copyright (C) 2011 Francesco Marella docs/formatV3*.txt: Copyright (c) 2003-2011 Rony Shapiro pasaffe_lib/figaroxml.py: Copyright (C) 2011-2012 Francesco Marella pasaffe_lib/keepassx.py: Copyright (C) 2011 Francesco Marella Copyright (C) 2012 Marc Deslauriers pasaffe_lib/pytwofish.py: Bjorn Edstrom 13 december 2007 Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999 pasaffe_lib/blowfish.py: Copyright (C) 2002 Michael Gilfix (c) 2007./08. Ivan Voras Joel Edwards tests/test_pasaffe-cli.py: Copyright (C) 2015 C de-Avillez ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/COPYING0000644000175000017500000010454300000000000015567 0ustar00mdeslaurmdeslaur00000000000000Unless otherwise *explicitly* stated, this license applies to the entire contents of this source tree: GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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 . ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.1520422 pasaffe-0.57/ChangeLog0000664000175000017500000043462400000000000016316 0ustar00mdeslaurmdeslaur000000000000002020-12-11 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.57 2020-12-11 Marc Deslauriers * NEWS: * tests/test_helpers.py: Fix some deprecation warnings in the test suite 2020-12-11 Marc Deslauriers * NEWS: * pasaffe_lib/Builder.py: Fixed deprecated ElementTree API (LP: #1907304) 2020-05-07 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2020-01-29 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2020-01-28 Marc Deslauriers * NEWS: prepare for next release 2020-01-28 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.56 2020-01-28 Marc Deslauriers * NEWS: * pasaffe_lib/helpers.py: * pasaffe_lib/readdb.py: * setup.py: * tests/test_helpers.py: * tests/test_readdb.py: fixed warnings with newer version of flake8 2019-11-16 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2019-11-15 Marc Deslauriers * NEWS: prepare for next release 2019-11-15 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.55 2019-11-15 Marc Deslauriers * README.development: README.development: pytest-3, not pyunit-3 2019-11-05 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2019-11-01 Marc Deslauriers * README.development: added README for developers 2019-11-01 Marc Deslauriers * tests/autopilot: * tests/autopilot/pasaffe: * tests/autopilot/pasaffe/__init__.py: * tests/autopilot/pasaffe/tests: * tests/autopilot/pasaffe/tests/__init__.py: * tests/autopilot/pasaffe/tests/test_pasaffe.py: * tests/test_example.py: * NEWS: * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/EditFolderDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: * pasaffe_lib/blowfish.py: * pasaffe_lib/figaroxml.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/helpers.py: * pasaffe_lib/helpersgui.py: * pasaffe_lib/keepassx.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/pytwofish.py: * pasaffe_lib/readdb.py: * setup.py: * tests/generate_pasaffe_db.py: * tests/test_dump_db.py: * tests/test_figaro_079.py: * tests/test_figaro_import.py: * tests/test_gpass_050.py: * tests/test_gpass_import.py: * tests/test_helpers.py: * tests/test_import_entry.py: * tests/test_keepass2_224.py: * tests/test_keepass2_import.py: * tests/test_keepassx_043.py: * tests/test_keepassx_import.py: * tests/test_lint.py: * tests/test_pasaffe_025.py: * tests/test_passwdsafe_510.py: * tests/test_pw_gorilla_15363.py: * tests/test_pw_gorilla_1537.py: * tests/test_pwsafe_331.py: * tests/test_readdb.py: do some pep 8 cleanup 2019-10-28 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: Display passwords in right pane in a mono font 2019-10-24 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2019-10-23 Marc Deslauriers * NEWS: prepare for next release 2019-10-23 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.54 2019-09-26 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/fr.po: * po/ru.po: Launchpad automatic translations update. 2019-09-25 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2019-09-24 Marc Deslauriers * NEWS: NEWS: wrap some long lines 2019-09-24 Marc Deslauriers * po/pasaffe.pot: regenerate po 2019-09-24 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: display password fields in a monospace font 2019-09-24 Marc Deslauriers * NEWS: * data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml: * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: Added configuration options for double-click behaviour and displaying usernames in the tree. 2019-09-24 Marc Deslauriers * NEWS: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: * po/pasaffe.pot: * setup.py: Merged branch from Christophe Kamphaus. Thanks! Christophe Kamphaus 2019-09-23 Added the clipboard clearing feature to t... Christophe Kamphaus 2019-09-23 Clear clipboard when locking or closi... Christophe Kamphaus 2019-09-22 prepare for next release Christophe Kamphaus 2019-09-22 release 0.53 Christophe Kamphaus 2019-09-22 Updated NEWS for next release Christophe Kamphaus 2019-09-22 Right key press goes to first child o... Christophe Kamphaus 2019-09-22 More robust folder expansion & collapse Christophe Kamphaus 2019-09-22 On enter, open password entry for edit Christophe Kamphaus 2019-09-22 Double click on tree item toggles fol... Christophe Kamphaus 2019-09-19 prepare for next release Christophe Kamphaus 2019-09-19 release 0.52 Christophe Kamphaus 2019-09-19 Added enhancements of last 2 commits ... Christophe Kamphaus 2019-09-19 Display username in the tree view ite... Christophe Kamphaus 2019-09-19 Show copy menu items first in entry c... Christophe Kamphaus 2019-09-19 Added change brought by merging lp:~a... Christophe Kamphaus 2019-09-19 [merge] Merged branch lp:~adrian-wilk... Adrian Wilkins 2019-04-16 Unicode multibyte chars no longer cause sh... 2019-09-23 Christophe Kamphaus * NEWS: Added the clipboard clearing feature to the NEWS 2019-09-23 Christophe Kamphaus * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Clear clipboard when locking or closing the window. Do not clear the clipboard if the user has copied something in another program. 2019-09-22 Christophe Kamphaus * NEWS: prepare for next release 2019-09-22 Christophe Kamphaus * NEWS: * po/pasaffe.pot: * setup.py: release 0.53 2019-09-22 Christophe Kamphaus * NEWS: Updated NEWS for next release 2019-09-22 Christophe Kamphaus * pasaffe/PasaffeWindow.py: Right key press goes to first child of expanded folder. Using set_cursor instead of select_iter when going to folder or uuid, because this results in the correct behavior when toggling folder expansion. goto_uuid now also correctly displays folders 2019-09-22 Christophe Kamphaus * pasaffe/PasaffeWindow.py: More robust folder expansion & collapse Left key press will collapse a folder or go to the parent folder Right key press will expand a folder 2019-09-22 Christophe Kamphaus * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: On enter, open password entry for edit 2019-09-22 Christophe Kamphaus * pasaffe/PasaffeWindow.py: Double click on tree item toggles folder expanded state and copies password. 2019-09-19 Christophe Kamphaus * NEWS: prepare for next release 2019-09-19 Christophe Kamphaus * NEWS: * po/pasaffe.pot: * setup.py: release 0.52 2019-09-19 Christophe Kamphaus * NEWS: Added enhancements of last 2 commits to the news * Menu items re-ordering in the tree context menu * Display username in the tree view item title 2019-09-19 Christophe Kamphaus * pasaffe/PasaffeWindow.py: Display username in the tree view item title Updates of the username also trigger a refresh of the tree 2019-09-19 Christophe Kamphaus * data/ui/PasaffeWindow.ui: Show copy menu items first in entry context menu 2019-09-19 Christophe Kamphaus * NEWS: Added change brought by merging lp:~adrian-wilkins/pasaffe/fix-unicode-short-buffer into the news file 2019-09-19 Christophe Kamphaus * pasaffe/PasaffeWindow.py: Merged branch lp:~adrian-wilkins/pasaffe/fix-unicode-short-buffer This fixes https://bugs.launchpad.net/pasaffe/+bug/1819141 2019-04-16 Adrian Wilkins * pasaffe/PasaffeWindow.py: Unicode multibyte chars no longer cause short buffers 2018-12-09 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2018-12-08 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2017-12-21 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-12-20 Marc Deslauriers * NEWS: prepare for next release 2017-12-20 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.51 2017-12-20 Marc Deslauriers * NEWS: updated NEWS 2017-12-20 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: switch folder icon to symbolic icon 2017-12-20 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-12-19 Marc Deslauriers * NEWS: prepare for next release 2017-12-19 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.50 2017-12-19 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: updated translations 2017-12-18 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-12-19 Marc Deslauriers * NEWS: * pasaffe/EditDetailsDialog.py: * pasaffe/PasaffeWindow.py: properly support popup menus on wayland 2017-12-17 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: updated translations 2017-12-17 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-12-16 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: updated translations 2017-12-16 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-12-16 Marc Deslauriers * NEWS: * data/ui/AboutPasaffeDialog.ui: * data/ui/EditDetailsDialog.ui: * data/ui/EditFolderDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/NewPasswordDialog.ui: * data/ui/PasaffeWindow.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/PreferencesPasaffeDialog.ui: * data/ui/SaveChangesDialog.ui: updated deprecated gtk widgets 2017-12-15 Marc Deslauriers * NEWS: * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: updated translations 2017-12-15 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-12-15 Marc Deslauriers * data/ui/PasaffeWindow.ui: - use document-properties-symbolic instead of document-edit-symbolic as the latter doesn't exist in xenial. 2017-12-14 Marc Deslauriers * NEWS: * data/ui/EditDetailsDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/NewPasswordDialog.ui: * data/ui/PasaffeWindow.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/SaveChangesDialog.ui: * pasaffe/PasaffeWindow.py: - switch to symbolic icons 2017-12-14 Marc Deslauriers * po/pasaffe.pot: regenerate po 2017-12-13 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: fix some untranslated strings 2017-12-13 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix markup in properties window 2017-12-13 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe/__init__.py: fix some pylint3 warnings 2017-12-13 Marc Deslauriers * NEWS: * pasaffe_lib/readdb.py: * tests/test_pasaffe_025.py: * tests/test_passwdsafe_510.py: * tests/test_pw_gorilla_15363.py: * tests/test_pw_gorilla_1537.py: * tests/test_pwsafe_331.py: * tests/test_readdb.py: fix bug where Pasaffe would think an entry needed saving when no fields actually changed. 2017-12-13 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: Tristan Hill 2017-12-13 only display file basename when not the default Tristan Hill 2017-11-17 text needs escaping in case of special chars Tristan Hill 2017-11-17 simplify Tristan Hill 2017-11-17 set window title from current filename 2017-12-13 Tristan Hill * pasaffe/PasaffeWindow.py: only display file basename when not the default 2017-11-17 Tristan Hill * pasaffe/PasaffeWindow.py: text needs escaping in case of special chars 2017-11-17 Tristan Hill * pasaffe/PasaffeWindow.py: simplify 2017-11-17 Tristan Hill * pasaffe/PasaffeWindow.py: set window title from current filename 2017-12-13 Marc Deslauriers * NEWS: * data/ui/PasswordEntryDialog.ui: make password dialog non-resizable 2017-05-19 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-05-17 Marc Deslauriers * NEWS: prepare for next release 2017-05-17 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.49 2017-05-12 Marc Deslauriers * NEWS: * setup.py: - setup.py: install mime file - NEWS: updated 2017-05-12 Marc Deslauriers * mime: * mime/pasaffe.xml: * pasaffe.desktop.in: * pasaffe/__init__.py: Tristan Hill 2016-11-21 add mime type and support for opening from file 2016-11-21 Tristan Hill * mime: * mime/pasaffe.xml: * pasaffe.desktop.in: * pasaffe/__init__.py: add mime type and support for opening from file manager 2017-05-12 Marc Deslauriers * NEWS: updated NEWS 2017-05-12 Marc Deslauriers * pasaffe_lib/readdb.py: Joshua C. Randall 2017-05-10 Raise ITER limit from 50k to 100k 2017-05-10 Joshua C. Randall * pasaffe_lib/readdb.py: Raise ITER limit from 50k to 100k 2017-03-15 Launchpad Translations on behalf of mdeslaur * po/en_GB.po: Launchpad automatic translations update. 2017-03-14 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2017-03-13 Marc Deslauriers * NEWS: prepare for next version 2017-03-13 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.48 2016-11-04 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-10-27 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - Fixed compatibility with GTK+ 3.20. Thanks to Mathieu Trudel-Lapierre for reporting the issue and for the initial patch. 2016-08-08 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2016-06-17 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-06-16 Marc Deslauriers * NEWS: prepare for next release 2016-06-16 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.47 2016-06-16 Marc Deslauriers * NEWS: * pasaffe_lib/helpersgui.py: don't crash if yelp is not installed 2016-06-13 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-06-12 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: * po/pasaffe.pot: * tests/test_readdb.py: properly handle blank entry and folder titles 2016-06-12 Marc Deslauriers * TODO: updated TODO 2016-06-12 Marc Deslauriers * po/pasaffe.pot: updated translations template 2016-06-12 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: * tests/test_readdb.py: fix error when opening database with missing entry titles (LP: #1586335) 2016-03-31 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2016-03-29 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-03-21 Marc Deslauriers * NEWS: added NEWS entry 2016-03-14 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-03-13 Marc Deslauriers * NEWS: prepare for next release 2016-03-13 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.46 2016-03-12 Marc Deslauriers * NEWS: added NEWS 2016-03-12 Marc Deslauriers * bin/pasaffe: * bin/pasaffe-cli: * bin/pasaffe-dump-db: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * pasaffe/PasaffeWindow.py: * pasaffe_lib/blowfish.py: * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/pytwofishcbc.py: * pasaffe_lib/readdb.py: * tests/generate_pasaffe_db.py: * tests/test_helpers.py: * tests/test_pasaffe-cli.py: * tests/test_readdb.py: pep8 cleanup 2016-03-12 Marc Deslauriers * pasaffe_lib/readdb.py: * tests/test_readdb.py: improve find algorithm, and added tests 2016-03-10 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: move find to readdb.py 2016-03-03 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-02-24 Marc Deslauriers * NEWS: * README: * pasaffe/PasaffeWindow.py: perform accent-insensitive searches (LP: #1488333) 2016-02-19 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-02-18 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2016-02-17 Marc Deslauriers * NEWS: prepare for next release 2016-02-17 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.45 2016-02-17 Marc Deslauriers * NEWS: * data/ui/PasaffeWindow.ui: stop using deprecated find icon 2016-02-17 Marc Deslauriers * NEWS: added NEWS entry 2016-02-17 Marc Deslauriers * pasaffe/EditDetailsDialog.py: * pasaffe/EditFolderDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/helpersgui.py: specify Gtk version when importing 2016-02-17 Marc Deslauriers * data/ui/AboutPasaffeDialog.ui: add author to about dialog 2016-02-12 Marc Deslauriers * TODO: add TODO item 2016-02-12 Marc Deslauriers * tests/test_pasaffe-cli.py: test_pasaffe-cli.py: don't use hardcoded /tmp directory 2016-02-12 Marc Deslauriers * pasaffe/EditDetailsDialog.py: pasaffe/EditDetailsDialog.py: add missing assignment 2016-02-12 Marc Deslauriers * AUTHORS: * NEWS: updated AUTHORS and NEWS 2016-02-12 Marc Deslauriers * bin/pasaffe-cli: * tests/test_pasaffe-cli.py: * pasaffe/EditDetailsDialog.py: * pasaffe_lib/helpers.py: * pasaffe_lib/readdb.py: Merge Pasaffe command line tool from C de-Avillez. Thanks! 2015-12-11 C de-Avillez * tests/test_pasaffe-cli.py: tests/test_pasaffe-cli.py: make sure there is no stale test DB before starting a test. 2015-12-10 C de-Avillez * tests/test_pasaffe-cli.py: tests/test_pasaffe-cli.py: readjusted code, prepare for testing lists. 2015-10-14 C de-Avillez * bin/pasaffe-cli: * tests/test_pasaffe-cli.py: * pasaffe/EditDetailsDialog.py: * pasaffe_lib/helpers.py: * pasaffe_lib/readdb.py: * tests/test_pasaffe-cli.py: QA tests for the CLI option of passaffe * pasaffe/EditDetailsDialog.py: adjust to call gen_password() instead of directly calling apg. * pasaffe_lib/helpers.py: add gen_password() function * pasaffe_lib/readdb.py: check os.name to decide on which password reader will be imported (preparation for a Windows port, at least for CLI). * bin/pasaffe-cli: command-line option for pasaffe. 2015-08-01 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2015-07-31 Marc Deslauriers * NEWS: prepare for next release 2015-07-30 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.44 2015-07-24 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2015-07-16 Marc Deslauriers * data/ui/AboutPasaffeDialog.ui: * data/ui/EditDetailsDialog.ui: * data/ui/EditFolderDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/NewPasswordDialog.ui: * data/ui/PasaffeWindow.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/PreferencesPasaffeDialog.ui: * data/ui/SaveChangesDialog.ui: don't hardcode icon location in ui 2015-06-27 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2015-06-26 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2015-06-18 Marc Deslauriers * data/icons: * data/icons/scalable: * data/icons/scalable/apps: * data/media: * NEWS: * pasaffe.desktop.in: * setup.py: * data/media/pasaffe.svg: * data/icons/scalable/apps/pasaffe.svg: No longer hardcode icon location (LP: #1459823) 2015-04-30 Launchpad Translations on behalf of mdeslaur * po/de.po: Launchpad automatic translations update. 2015-04-28 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2015-04-20 Marc Deslauriers * tests/test_pw_gorilla_15363.py: * tests/test_pw_gorilla_1537.py: fix some pep-8 cleanup 2015-04-20 Marc Deslauriers * tests/generate_pasaffe_db.py: * tests/test_dump_db.py: * tests/test_example.py: * tests/test_figaro_079.py: * tests/test_figaro_import.py: * tests/test_gpass_050.py: * tests/test_gpass_import.py: * tests/test_helpers.py: * tests/test_import_entry.py: * tests/test_keepass2_224.py: * tests/test_keepass2_import.py: * tests/test_keepassx_043.py: * tests/test_keepassx_import.py: * tests/test_lint.py: * tests/test_pasaffe_025.py: * tests/test_passwdsafe_510.py: * tests/test_pw_gorilla_15363.py: * tests/test_pw_gorilla_1537.py: * tests/test_pwsafe_331.py: * tests/test_readdb.py: more pep8 cleanups 2015-04-13 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2015-04-12 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2015-04-05 Marc Deslauriers * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/EditFolderDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: more PEP 8 cleanup 2015-04-05 Marc Deslauriers * bin/pasaffe: * bin/pasaffe-dump-db: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: more PEP 8 cleanup 2015-04-05 Marc Deslauriers * NEWS: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: * pasaffe_lib/blowfish.py: * pasaffe_lib/figaroxml.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/helpersgui.py: * pasaffe_lib/keepassx.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/pytwofish.py: * pasaffe_lib/readdb.py: start PEP 8 cleanup 2015-03-09 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2015-03-08 Marc Deslauriers * NEWS: prepare for next release 2015-03-08 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.43 2015-03-07 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - make sure left pane is focused on startup 2015-02-13 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2015-02-07 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2015-02-05 Marc Deslauriers * NEWS: prepare for next release 2015-02-05 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.42 2015-02-05 Marc Deslauriers * NEWS: updated NEWS 2015-02-05 Marc Deslauriers * data/ui/PreferencesPasaffeDialog.ui: remove more deprecated gtk stuff 2015-02-05 Marc Deslauriers * data/ui/PasswordEntryDialog.ui: removed deprecated ypad property 2015-02-05 Marc Deslauriers * pasaffe_lib/Window.py: make sure about dialog has a transient parent 2014-11-04 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-11-03 Marc Deslauriers * NEWS: prepare for next release 2014-11-03 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.41 2014-11-03 Marc Deslauriers * NEWS: added to NEWS 2014-11-03 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: merged translations 2014-11-03 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-11-03 Marc Deslauriers * NEWS: * pasaffe_lib/readdb.py: * tests/test_readdb.py: fixed regression in notes in version 0.40 2014-11-02 Marc Deslauriers * NEWS: prepare for next release 2014-11-02 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.40 2014-11-02 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: disable menu and toolbar entries for missing fields 2014-10-30 Launchpad Translations on behalf of mdeslaur * po/en_GB.po: * po/fr.po: Launchpad automatic translations update. 2014-10-29 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-10-28 Marc Deslauriers * po/pasaffe.pot: updated translation template 2014-10-28 Marc Deslauriers * NEWS: * data/ui/EditDetailsDialog.ui: * data/ui/EditFolderDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/NewPasswordDialog.ui: * data/ui/PasaffeWindow.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/PreferencesPasaffeDialog.ui: * data/ui/SaveChangesDialog.ui: stop using deprecated gtk stock items 2014-10-27 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: * pasaffe_lib/Window.py: make sure all dialogs have transient parents 2014-10-25 Marc Deslauriers * NEWS: updated NEWS file 2014-10-25 Marc Deslauriers * tests/test_readdb.py: tests/test_readdb.py: added tests for fixup code 2014-10-25 Marc Deslauriers * bin/pasaffe-dump-db: * docs/interopability.txt: * pasaffe_lib/readdb.py: * tests/test_passwdsafe_510.py: * tests/test_pw_gorilla_15363.py: * tests/test_pw_gorilla_1537.py: * tests/test_pwsafe_331.py: docs/interopability.txt: added more interopability notes about usernames pasaffe_lib/readdb.py: added fixup functions to clean up and convert databases on open and before save bin/pasaffe-dump-db: don't clean up when dumping raw tests/*: updated now that cleanup is being performed 2014-10-25 Marc Deslauriers * tests/test_passwdsafe_510.py: * docs/interopability.txt: added passwdsafe 5.1.0 test script added more interopability notes 2014-10-25 Marc Deslauriers * tests/databases/passwdsafe-510.psafe3: * tests/databases/README: added passwdsafe test database 2014-10-25 Marc Deslauriers * docs/interopability.txt: added more interopability notes 2014-10-25 Marc Deslauriers * bin/pasaffe-dump-db: added raw dump option to pasaffe-dump-db 2014-10-25 Marc Deslauriers * docs/interopability.txt: added notes about password and username fields 2014-10-25 Marc Deslauriers * tests/test_pw_gorilla_1537.py: added test for new password gorilla database 2014-10-25 Marc Deslauriers * tests/databases/pw-gorilla-1537.psafe3: * tests/databases/README: added new password gorilla test database 2014-09-28 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2014-09-22 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2014-08-03 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2014-08-02 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2014-08-01 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-07-31 Marc Deslauriers * NEWS: prepare for next release 2014-07-31 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.39 2014-07-31 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: edit button in toolbar now also edits folders 2014-05-29 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-05-21 Marc Deslauriers * NEWS: * data/ui/EditDetailsDialog.ui: * data/ui/EditFolderDialog.ui: * pasaffe/PasaffeWindow.py: corrected title in new entry and new folder windows (LP: #1314950) 2014-05-10 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-05-01 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: improve database information window 2014-05-01 Marc Deslauriers * NEWS: * help/C/delete-entry.page: * help/C/index.page: * help/C/legal.xml: * help/C/new-entry.page: * help/C/preferences.page: * help/C/show-secrets.page: * help/fr/delete-entry.page: * help/fr/index.page: * help/fr/legal.xml: * help/fr/new-entry.page: * help/fr/preferences.page: * help/fr/show-secrets.page: fixed help file string and text 2014-04-17 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-04-09 Marc Deslauriers * NEWS: * pasaffe/EditDetailsDialog.py: make password generation more random 2014-04-05 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-04-04 Marc Deslauriers * NEWS: prepare for next release 2014-04-04 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.38 2014-04-04 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: Launchpad automatic translations update. 2014-04-03 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-04-02 Marc Deslauriers * po/pasaffe.pot: updated pot file 2014-04-02 Marc Deslauriers * NEWS: * help/fr/delete-entry.page: * help/fr/new-entry.page: * help/fr/preferences.page: * help/fr/show-secrets.page: added some missing translations (LP: #1300012) 2014-03-31 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: allow 'new folder' to be translated 2014-02-16 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-15 Marc Deslauriers * NEWS: prepare for next release 2014-02-15 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.37 2014-02-15 Marc Deslauriers * NEWS: fix version number in NEWS 2014-02-15 Marc Deslauriers * NEWS: * po/pasaffe.pot: release 0.36 2014-02-15 Marc Deslauriers * NEWS: updated NEWS file 2014-02-15 Marc Deslauriers * NEWS: * TODO: * pasaffe_lib/figaroxml.py: * tests/test_figaro_079.py: figaro script now handles folders 2014-02-15 Marc Deslauriers * tests/databases/keepass2-224.kdbx: * tests/databases/keepass2-224.xml: * tests/test_keepass2_224.py: * tests/test_keepass2_import.py: * NEWS: * pasaffe_lib/keepassx.py: * tests/databases/README: aded keepass2 tests, and fixed import script 2014-02-15 Marc Deslauriers * NEWS: * pasaffe_lib/keepassx.py: * tests/test_keepassx_043.py: keepassx import script now handles folders 2014-02-13 Marc Deslauriers * NEWS: * bin/pasaffe-import-gpass: * pasaffe_lib/gpassfile.py: * tests/databases/gpass-050.gps: * tests/test_gpass_050.py: gpass import support now handles folders 2014-02-13 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-12 Marc Deslauriers * NEWS: prepare for next release 2014-02-12 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.36 2014-02-12 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-11 Marc Deslauriers * NEWS: * po/pasaffe.pot: regenerated pot file 2014-02-11 Marc Deslauriers * NEWS: * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: improved right pane layout 2014-02-10 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-09 Marc Deslauriers * NEWS: prepare for next release 2014-02-09 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.35 2014-02-09 Marc Deslauriers * tests/databases/figaro-079.xml: * tests/test_figaro_079.py: * tests/test_figaro_import.py: * bin/pasaffe-import-figaroxml: * pasaffe_lib/figaroxml.py: * tests/databases/README: migrate figaro import scripts to python 3, added test databases and tests. 2014-02-09 Marc Deslauriers * tests/databases/keepassx-043.kdb: * tests/databases/keepassx-043.xml: * tests/test_keepassx_043.py: * tests/test_keepassx_import.py: * bin/pasaffe-import-keepassx: * pasaffe_lib/keepassx.py: * tests/databases/README: migrate keepassx handling to python3, and add some test databases and test scripts 2014-02-09 Marc Deslauriers * bin/pasaffe-import-entry: * tests/test_import_entry.py: fix pasaffe-import-entry and added test 2014-02-09 Marc Deslauriers * tests/test_dump_db.py: * bin/pasaffe-dump-db: updated pasaffe-dump-db and added test 2014-02-09 Marc Deslauriers * TODO: * po/fr.po: - added help file translations to TODO file - removed my name from fr.po 2014-02-08 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-07 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-06 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: - updated translations 2014-02-06 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-04 Marc Deslauriers * po/fr.po: * po/pasaffe.pot: - updated translation templates 2014-02-04 Marc Deslauriers * NEWS: updated NEWS 2014-02-04 Marc Deslauriers * data/ui/AboutPasaffeDialog.ui: - add translator-credits to about dialog (LP: #1273673) 2014-02-04 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: merged translations 2014-01-30 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-02-04 Marc Deslauriers * tests/test_import_entry.py: actually add test_import_entry.py file 2014-02-03 Marc Deslauriers * tests/test_lint.py: tests/test_lint.py: fix whitespace 2014-02-03 Marc Deslauriers * tests/test_gpass_import.py: * .bzrignore: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: added tests/__pycache__ to .bzrignore added gpass import testing script fix import scripts 2014-01-28 Marc Deslauriers * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * pasaffe/__init__.py: * pasaffe_lib/helpers.py: refactor some import script code 2014-01-27 Marc Deslauriers * tests/databases/gpass-050.gps: * tests/test_gpass_050.py: * pasaffe_lib/blowfish.py: * pasaffe_lib/gpassfile.py: * tests/databases/README: migrate gpass import code to python 3 2014-01-23 Marc Deslauriers * setup.py: setup.py: use open() instead of file() 2014-01-23 Marc Deslauriers * setup.py: setup.py: specify python3 2014-01-23 Marc Deslauriers * do-release: do-release: also remove python cache directories 2014-01-23 Marc Deslauriers * pasaffe/EditDetailsDialog.py: pasaffe/EditDetailsDialog.py: convert apg results to strings 2014-01-23 Marc Deslauriers * NEWS: * README: * TODO: updated NEWS, README and TODO 2014-01-23 Marc Deslauriers * pasaffe_lib/helpers.py: * pasaffe_lib/readdb.py: * tests/test_pasaffe_025.py: * tests/test_pw_gorilla_15363.py: * tests/test_pwsafe_331.py: more python 3 porting, change uuid to string instead of bytes 2014-01-23 Marc Deslauriers * tests/test_pwsafe_331.py: test/test_pwsafe_331.py: migrate to python 3 2014-01-23 Marc Deslauriers * tests/test_pw_gorilla_15363.py: tests/test_pw_gorilla_15363.py: convert to python 3 2014-01-23 Marc Deslauriers * pasaffe_lib/helpers.py: pasaffe_lib/helpers.py: convert to python 3 2014-01-23 Marc Deslauriers * pasaffe_lib/pytwofishcbc.py: * pasaffe_lib/readdb.py: * tests/test_pasaffe_025.py: more python3 porting 2014-01-22 Marc Deslauriers * pasaffe_lib/readdb.py: * tests/test_readdb.py: pasaffe_lib/readdb.py, tests/test_readdb.py: properly handle bytes and strings for python 3 compatibility 2014-01-22 Marc Deslauriers * pasaffe_lib/pytwofish.py: pasaffe_lib/pytwofish.py: fix with python 3 2014-01-22 Marc Deslauriers * pasaffe/__init__.py: pasaffe/__init__.py: fix gettext 2014-01-22 Marc Deslauriers * .bzrignore: update .bzrignore for python3 cache directories 2014-01-22 Marc Deslauriers * setup.py: run 2to3 on setup.py 2014-01-22 Marc Deslauriers * pasaffe_lib/Builder.py: * pasaffe_lib/blowfish.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/pytwofish.py: * pasaffe_lib/pytwofishcbc.py: * pasaffe_lib/readdb.py: run 2to3 on pasaffe_lib directory 2014-01-22 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe/__init__.py: run 2to3 on pasaffe directory 2014-01-22 Marc Deslauriers * bin/pasaffe-dump-db: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: run 2to3 on bin directory 2014-01-22 Marc Deslauriers * bin/pasaffe: * bin/pasaffe-dump-db: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * tests/autopilot/pasaffe/tests/test_pasaffe.py: * tests/generate_pasaffe_db.py: * tests/test_example.py: * tests/test_helpers.py: * tests/test_lint.py: * tests/test_pasaffe_025.py: * tests/test_pw_gorilla_15363.py: * tests/test_pwsafe_331.py: * tests/test_readdb.py: - start migration to python3 2014-01-19 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/ko.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2014-01-18 Marc Deslauriers * NEWS: prepare for next release 2014-01-18 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.34 2014-01-18 Marc Deslauriers * NEWS: * pasaffe/EditDetailsDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: * pasaffe_lib/Window.py: Fix new PyGI deprecations 2013-11-21 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2013-11-20 Launchpad Translations on behalf of mdeslaur * po/ko.po: Launchpad automatic translations update. 2013-10-06 Launchpad Translations on behalf of mdeslaur * po/de.po: Launchpad automatic translations update. 2013-10-05 Launchpad Translations on behalf of mdeslaur * po/de.po: Launchpad automatic translations update. 2013-10-03 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-10-02 Marc Deslauriers * NEWS: prepare for next release 2013-10-02 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.33 2013-10-02 Marc Deslauriers * NEWS: * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: updated translations 2013-09-28 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2013-09-27 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-09-26 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: don't crash when search string ends with a backslash 2013-09-26 Marc Deslauriers * NEWS: prepare for next release 2013-09-26 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.32 2013-09-26 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: allow search to work on entries in collapsed folders 2013-09-26 Marc Deslauriers * NEWS: prepare for next release 2013-09-26 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.31 2013-09-26 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - don't crash when attempting to add an entry with nothing selected 2013-09-26 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-09-09 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-09-08 Marc Deslauriers * NEWS: prepare for next release 2013-09-08 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.30 2013-09-08 Marc Deslauriers * NEWS: * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-09-08 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-09-07 Marc Deslauriers * setup.py: properly quote config items 2013-09-07 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-09-07 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-09-07 Marc Deslauriers * pasaffe_lib/helpers.py: fix typo 2013-09-07 Marc Deslauriers * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: * setup.py: also handle help file separator 2013-09-07 Marc Deslauriers * NEWS: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: * setup.py: fix help file install location when using newer python-distutils-extra 2013-09-06 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-09-06 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-09-05 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-09-06 Marc Deslauriers * TODO: added a TODO 2013-09-04 Marc Deslauriers * NEWS: prepare for next release 2013-09-04 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.29 2013-09-04 Marc Deslauriers * pasaffe/PasaffeWindow.py: remove MessageDialog deprecation warning 2013-09-04 Marc Deslauriers * TODO: * data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml: * pasaffe/PasaffeWindow.py: default to collapsing folders 2013-09-04 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-31 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-30 Marc Deslauriers * NEWS: * po/fr.po: updated translations 2013-08-30 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-30 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: expand folder if we're adding an entry 2013-08-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix collapsed state if no tree info in database 2013-08-29 Marc Deslauriers * NEWS: updated NEWS 2013-08-29 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-29 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-28 Marc Deslauriers * help/fr/preferences.page: updated french help 2013-08-28 Marc Deslauriers * po/pasaffe.pot: updated translation template 2013-08-28 Marc Deslauriers * help/C/preferences.page: added new preference to help 2013-08-28 Marc Deslauriers * data/ui/PreferencesPasaffeDialog.ui: change wording 2013-08-28 Marc Deslauriers * data/ui/PreferencesPasaffeDialog.ui: realign preferences dialog 2013-08-27 Marc Deslauriers * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PreferencesPasaffeDialog.py: fixed tree expansion preference binding 2013-08-21 Marc Deslauriers * NEWS: updated NEWS 2013-08-21 Marc Deslauriers * data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml: * pasaffe/PasaffeWindow.py: save folder window size 2013-08-21 Marc Deslauriers * data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml: * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: added initial preference for tree expansion 2013-08-21 Marc Deslauriers * pasaffe_lib/readdb.py: * tests/test_helpers.py: * tests/test_readdb.py: added some tests 2013-08-21 Marc Deslauriers * NEWS: * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: * pasaffe_lib/helpers.py: * pasaffe_lib/readdb.py: implement folder expansion saving to database 2013-08-20 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-19 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-17 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-16 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-16 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-16 Marc Deslauriers * NEWS: * data/ui/PasaffeWindow.ui: Set icon column sizing to automatic so tree doesn't move around 2013-08-15 Marc Deslauriers * NEWS: prepare for next release 2013-08-15 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.28 2013-08-15 Marc Deslauriers * NEWS: fixed version in NEWS 2013-08-15 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-15 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-15 Marc Deslauriers * NEWS: * pasaffe_lib/helpers.py: * tests/test_helpers.py: use case-insensitive sort in tree 2013-08-14 Marc Deslauriers * help/C/legal.xml: * help/fr/legal.xml: * help/C/index.page: * help/fr/index.page: fix some translation issues in the help files 2013-08-14 Marc Deslauriers * po/pasaffe.pot: updated pot file 2013-08-14 Marc Deslauriers * NEWS: updated NEWS 2013-08-14 Marc Deslauriers * data/ui/PasaffeWindow.ui: mark some more menu entries as translatable 2013-08-14 Marc Deslauriers * TODO: * help/C/new-entry.page: * help/C/preferences.page: * help/fr/new-entry.page: * help/fr/preferences.page: updated the help files 2013-08-14 Marc Deslauriers * TODO: added a TODO 2013-08-14 Marc Deslauriers * data/ui/AboutPasaffeDialog.ui: Changed About dialog to use a real copyright character 2013-08-14 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-13 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-14 Marc Deslauriers * NEWS: * pasaffe_lib/Window.py: fix crash when changing the show secrets setting 2013-08-12 Marc Deslauriers * NEWS: * help/fr/delete-entry.page: * help/fr/index.page: * help/fr/new-entry.page: * help/fr/preferences.page: * help/fr/show-secrets.page: Fixed some translation mistakes in the fr help file (LP: #1210552) 2013-08-12 Marc Deslauriers * NEWS: prepare for next release 2013-08-12 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.27 2013-08-12 Marc Deslauriers * pasaffe/PasaffeWindow.py: don't allow dragging a folder into a subdirectory of itself 2013-08-12 Marc Deslauriers * NEWS: * TODO: * pasaffe/PasaffeWindow.py: deactivate certain menu items when a folder is selected 2013-08-12 Marc Deslauriers * NEWS: * data/ui/PasaffeWindow.ui: fix broken menu entries when gtk is configured to show icons in menus 2013-08-12 Marc Deslauriers * NEWS: * TODO: * pasaffe/PasaffeWindow.py: implemented drag and drop 2013-08-12 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: updated translations 2013-08-12 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-11 Marc Deslauriers * NEWS: prepared for next release 2013-08-11 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.26 2013-08-11 Marc Deslauriers * NEWS: * data/ui/PasaffeWindow.ui: fixed typeahead find (thanks to hallyn for reporting!) 2013-08-11 Marc Deslauriers * NEWS: updated NEWS 2013-08-11 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: updated translations 2013-08-10 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-11 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/helpers.py: * tests/test_helpers.py: - sort folders before names (thanks to jdstrand for reporting) 2013-08-09 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/helpers.py: move PathEntry class to helpers.py 2013-08-09 Marc Deslauriers * NEWS: * data/ui/EditFolderDialog.ui: - folder text entry now activates OK button (thanks jdstrand!) 2013-08-09 Marc Deslauriers * NEWS: prepared for next release 2013-08-09 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.25 2013-08-09 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix menu hiding when locked 2013-08-09 Marc Deslauriers * NEWS: added NEWS 2013-08-09 Marc Deslauriers * help/fr/index.page: - fixed a french translation issue 2013-08-09 Marc Deslauriers * data/ui/SaveChangesDialog.ui: fixed untranslated string (LP: #1202980) 2013-08-09 Marc Deslauriers * po/fr.po: updated french translation 2013-08-09 Marc Deslauriers * TODO: added TODO 2013-08-09 Marc Deslauriers * pasaffe/PasaffeWindow.py: Don't allow open URL on a folder 2013-08-09 Marc Deslauriers * TODO: added some TODOs 2013-08-09 Marc Deslauriers * pasaffe/PasaffeWindow.py: Don't allow copying stuff from folders 2013-08-09 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix removing last entry 2013-08-06 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-05 Marc Deslauriers * tests/autopilot: * tests/autopilot/pasaffe: * tests/autopilot/pasaffe/__init__.py: * tests/autopilot/pasaffe/tests: * tests/autopilot/pasaffe/tests/__init__.py: * tests/autopilot/pasaffe/tests/test_pasaffe.py: - added autopilot test 2013-08-05 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-08-04 Launchpad Translations on behalf of mdeslaur * po/en_GB.po: Launchpad automatic translations update. 2013-08-03 Launchpad Translations on behalf of mdeslaur * po/en_GB.po: Launchpad automatic translations update. 2013-08-02 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-08-01 Marc Deslauriers * pasaffe/PasaffeWindow.py: don't die if trying to clone a folder 2013-08-01 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: add add folder entry to menus 2013-08-01 Marc Deslauriers * po/pasaffe.pot: regenerated pot file 2013-08-01 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-31 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-30 Marc Deslauriers * tests/databases/pasaffe-025.psafe3: * tests/generate_pasaffe_db.py: * tests/test_pasaffe_025.py: * pasaffe_lib/readdb.py: * tests/databases/README: * tests/test_pw_gorilla_15363.py: * tests/test_pwsafe_331.py: - added pasaffe test database - added test database generation script - readdb.py: allow requesting time in UTC, for testing - modified test scripts to use UTC time 2013-07-30 Marc Deslauriers * TODO: * data/ui/EditDetailsDialog.ui: fix tab issues 2013-07-30 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-28 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-30 Marc Deslauriers * tests/test_helpers.py: * pasaffe_lib/helpers.py: added test for helpers, and fix broken logic 2013-07-30 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/helpers.py: move conversion functions to helper lib 2013-07-30 Marc Deslauriers * pasaffe/EditFolderDialog.py: EditFolderDialog.py: fix pylint warning 2013-07-27 Marc Deslauriers * data/ui/PasaffeWindow.ui: add icon alignment, not sure what best default is yet 2013-07-27 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: added icons to tree 2013-07-27 Marc Deslauriers * TODO: added a TODO 2013-07-27 Marc Deslauriers * docs/interopability.txt: * TODO: - added a file detailing interopability issues 2013-07-27 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-26 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-27 Marc Deslauriers * pasaffe_lib/readdb.py: * tests/test_pw_gorilla_15363.py: * tests/test_pwsafe_331.py: - added more database tests 2013-07-25 Marc Deslauriers * tests/test_pw_gorilla_15363.py: * tests/test_pwsafe_331.py: - start adding database tests 2013-07-25 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-25 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-25 Marc Deslauriers * tests/databases/pw-gorilla-15363.psafe3: * tests/databases/pwsafe-331.psafe3: - updated test databases 2013-07-24 Marc Deslauriers * tests/databases/pw-gorilla-15363.psafe3: * tests/databases/README.txt: * tests/databases/README: - added a Password Gorilla test database 2013-07-24 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-24 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-24 Marc Deslauriers * tests/databases: * tests/databases/README.txt: * tests/databases/pwsafe-331.psafe3: * examples: * examples/README: * examples/pasaffe.psafe3: - removed example database from examples directory - added new example database to tests/databases 2013-07-23 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-23 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-23 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix adding a new entry with no folder selected 2013-07-23 Marc Deslauriers * data/ui/EditFolderDialog.ui: * pasaffe/PasaffeWindow.py: added parent folder drop-down to edit folder dialog 2013-07-22 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-22 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-22 Marc Deslauriers * pasaffe_lib/readdb.py: * tests/test_readdb.py: test_readdb.py: added more tests readdb.py: fix a couple of issues 2013-07-20 Marc Deslauriers * tests/test_readdb.py: more tests 2013-07-20 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-20 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-20 Marc Deslauriers * pasaffe_lib/readdb.py: * tests/test_readdb.py: added more tests 2013-07-20 Marc Deslauriers * NEWS: * TODO: * tests/test_readdb.py: added more tests 2013-07-19 Marc Deslauriers * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged translations 2013-07-12 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/en_GB.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-19 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: fixed some more empty folder issues added some debug output 2013-07-13 Marc Deslauriers * tests/test_readdb.py: * pasaffe_lib/readdb.py: added some readdb tests 2013-07-11 Marc Deslauriers * pasaffe_lib/readdb.py: fix bug with folder unescaping 2013-07-11 Marc Deslauriers * po/en_GB.po: * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: merged 2013-07-10 Launchpad Translations on behalf of mdeslaur * po/en_GB.po: Launchpad automatic translations update. 2013-07-09 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-11 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - improve folder support 2013-07-08 Marc Deslauriers * TODO: * data/ui/EditDetailsDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - start implementing folder selection drop-down 2013-07-08 Marc Deslauriers * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: - merged translations - fixed removing an empty folder 2013-07-07 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-07-08 Marc Deslauriers * pasaffe_lib/readdb.py: fix removing an empty folder 2013-07-04 Marc Deslauriers * pasaffe_lib/readdb.py: readdb.py: get rid of example data 2013-07-04 Marc Deslauriers * docs/formatV3.txt: * AUTHORS: * TODO: * pasaffe_lib/readdb.py: * docs/formatV3.txt: * docs/formatV322.txt: implement empty folder saving and loading from db 2013-07-03 Marc Deslauriers * TODO: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: further work on folder support 2013-07-03 Marc Deslauriers * pasaffe/PasaffeWindow.py: further work on folder support 2013-07-03 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix adding empty folders 2013-07-03 Marc Deslauriers * pasaffe/PasaffeWindow.py: allow creating a new entry in an existing folder 2013-07-02 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix cancelling a new item 2013-07-02 Marc Deslauriers * data/ui/EditFolderDialog.ui: * data/ui/edit_folder_dialog.xml: * pasaffe/PasaffeWindow.py: more work on folder edit dialog 2013-06-29 Marc Deslauriers * pasaffe/EditFolderDialog.py: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: start an edit folder dialog 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: make the new highlight more predictible when we delete an item 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: improve search_tree 2013-06-29 Marc Deslauriers * TODO: added some folder TODO items 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: properly handle deleting an item in a folder 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: added recursive search into tree 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: allow specifying a uuid to switch to when refreshing the tree 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: fix some whitespace 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: Properly display folders with dots in their name 2013-06-29 Marc Deslauriers * data/ui/EditDetailsDialog.ui: * pasaffe/PasaffeWindow.py: Prefer "Folder" to "Path" 2013-06-29 Marc Deslauriers * pasaffe/PasaffeWindow.py: PasaffeWindow.py: allow sorting entries with no folder 2013-06-29 Marc Deslauriers * pasaffe_lib/readdb.py: pasaffe_lib/readdb.py: Don't force folder names to be lowercase. The database format allows different independently cased folder names. Also, remove some debugging prints. 2013-06-29 Marc Deslauriers * NEWS: * TODO: Updated NEWS and TODO 2013-06-29 Marc Deslauriers * data/ui/EditDetailsDialog.ui: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: Merged tree support: - Jabik Postmus 2013-06-28 Added support for a tree view of your passwords 2013-06-28 Jabik Postmus * data/ui/EditDetailsDialog.ui: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: Added support for a tree view of your passwords 2013-06-03 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-06-02 Marc Deslauriers * AUTHORS: * bin/pasaffe-import-figaroxml: * data/ui/AboutPasaffeDialog.ui: * pasaffe_lib/figaroxml.py: * pasaffe_lib/keepassx.py: * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: - updated translations - merged Francesco Marella's email address change 2013-06-02 Francesco Marella * AUTHORS: * bin/pasaffe-import-figaroxml: * data/ui/AboutPasaffeDialog.ui: * pasaffe_lib/figaroxml.py: * pasaffe_lib/keepassx.py: updated email address 2013-03-28 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2013-03-07 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-03-06 Marc Deslauriers * AUTHORS: updated copyright date in AUTHORS file 2013-03-06 Marc Deslauriers * NEWS: - fix version for next release 2013-03-06 Marc Deslauriers * NEWS: Prepare for next release 2013-03-06 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.24 2013-02-17 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-02-16 Marc Deslauriers * NEWS: - added NEWS 2013-02-16 Marc Deslauriers * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: - merged translations 2013-02-15 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2013-02-13 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2013-02-16 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: removed deprecated GObject usage 2013-02-16 Marc Deslauriers * NEWS: * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * tests/test_example.py: fix pylint warnings and test suite 2013-02-12 Marc Deslauriers * NEWS: - prepare for next release 2013-02-12 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.23 2013-02-12 Marc Deslauriers * po/fr.po: merged new translations 2013-02-10 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2013-02-12 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: Fixed adding new entries 2013-02-09 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.22 2013-02-09 Marc Deslauriers * AUTHORS: * bin/pasaffe: * bin/pasaffe-import-entry: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * data/ui/AboutPasaffeDialog.ui: * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/helpers.py: * pasaffe_lib/helpersgui.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/pytwofishcbc.py: * pasaffe_lib/readdb.py: * setup.py: * tests/test_example.py: * tests/test_lint.py: Updated copyright notices 2013-02-09 Marc Deslauriers * pasaffe_lib/helpersgui.py: * NEWS: Added missing file, and update NEWS 2013-02-09 Marc Deslauriers * pasaffe/EditDetailsDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: * pasaffe_lib/helpers.py: Move gui elements from pasaffe_lib/helpers.py to helpersgui.py so command line tools such as pasaffe-dump-db can be used in an ssh session. 2013-02-09 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2013-01-10 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2013-01-05 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-12-28 Marc Deslauriers * NEWS: updated NEWS 2012-12-28 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - added new time functions to readdb.py - added new folder functions to readdb.py - updated PasaffeWindow.py to use new time functions 2012-12-17 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2012-12-16 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2012-11-28 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2012-11-04 Launchpad Translations on behalf of mdeslaur * po/de.po: Launchpad automatic translations update. 2012-10-26 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-10-24 Launchpad Translations on behalf of mdeslaur * po/de.po: Launchpad automatic translations update. 2012-10-07 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2012-10-06 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2012-09-12 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-09-04 Marc Deslauriers * setup.py: setup.py: fix some whitespace 2012-09-04 Marc Deslauriers * pasaffe.desktop.in: - Fix icon location in desktop file 2012-08-28 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-08-21 Marc Deslauriers * NEWS: Prepare for next release 2012-08-21 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.21 2012-08-20 Marc Deslauriers * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: * pasaffe_lib/blowfish.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/helpers.py: * pasaffe_lib/keepassx.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/pytwofish.py: * pasaffe_lib/pytwofishcbc.py: * pasaffe_lib/readdb.py: - fix more pep8 warnings 2012-08-18 Marc Deslauriers * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: Fix some pep8 warnings 2012-08-17 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - Added new_entry() and use it 2012-08-17 Marc Deslauriers * bin/pasaffe-dump-db: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * pasaffe_lib/figaroxml.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/keepassx.py: - Also migrate import scripts 2012-08-17 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: Make record entries a dict with the hex encoded uuid as the key. This simplifies the code greatly and makes look ups easier 2012-08-17 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - more code cleanup 2012-08-17 Marc Deslauriers * bin/pasaffe-import-entry: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/__init__.py: * pasaffe_lib/figaroxml.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/pytwofishcbc.py: * pasaffe_lib/readdb.py: - Updated copyright notices 2012-08-17 Marc Deslauriers * pasaffe_lib/readdb.py: - Start much needed code cleanup 2012-08-16 Marc Deslauriers * NEWS: * TODO: * data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - Remember window sizes 2012-08-16 Marc Deslauriers * TODO: Added window sizes to TODO 2012-08-16 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-08-15 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-08-14 Marc Deslauriers * NEWS: Prepare for next release 2012-08-14 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.20 2012-08-14 Marc Deslauriers * do-release: do-release: remove compiled gschema file before building tarball 2012-08-11 Marc Deslauriers * data/ui/PreferencesPasaffeDialog.ui: Improve preference dialog layout 2012-08-11 Marc Deslauriers * data/ui/AboutPasaffeDialog.ui: * data/ui/EditDetailsDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/NewPasswordDialog.ui: * data/ui/PasaffeWindow.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/PreferencesPasaffeDialog.ui: * data/ui/SaveChangesDialog.ui: - updated glade files to gtk 3 2012-08-11 Marc Deslauriers * NEWS: * TODO: * data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml: * pasaffe/PasaffeWindow.py: - Automatically clear clipboard 2012-08-11 Marc Deslauriers * .bzrignore: * data/glib-2.0: * data/glib-2.0/schemas: * data/pasaffe.convert: * setup.cfg: * pasaffe_lib/__init__.py: * setup.py: * data/net.launchpad.pasaffe.gschema.xml: * data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml: - Removed lucid compatibility - Moved schema file to location where quickly can find it 2012-08-11 Marc Deslauriers * NEWS: - Updated NEWS 2012-08-11 Marc Deslauriers * data/pasaffe.convert: * pasaffe/EditDetailsDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: * pasaffe_lib/Window.py: * setup.cfg: * data/apps.pasaffe.gschema.xml: * data/net.launchpad.pasaffe.gschema.xml: - Move gsettings schema from apps.pasaffe to net.launchpad.pasaffe 2012-08-11 Marc Deslauriers * .quickly: - Update quickly 2012-08-10 Marc Deslauriers * TODO: Added clipboard TODO item 2012-07-22 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-07-14 Marc Deslauriers * pasaffe/EditDetailsDialog.py: - Set a fixed password length 2012-07-14 Marc Deslauriers * NEWS: * data/apps.pasaffe.gschema.xml: * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/EditDetailsDialog.py: * pasaffe/PreferencesPasaffeDialog.py: - Make password length configurable 2012-07-10 Mathieu Trudel-Lapierre * pasaffe/EditDetailsDialog.py: - Must import Gio for access to gsettings. - password_length must be passed to check_output as string. 2012-07-10 Mathieu Trudel-Lapierre * data/ui/PreferencesPasaffeDialog.ui: Add the UI bits for password length selection. 2012-07-10 Mathieu Trudel-Lapierre * data/apps.pasaffe.gschema.xml: * pasaffe/EditDetailsDialog.py: * pasaffe/PreferencesPasaffeDialog.py: Add support for setting password length. 2012-05-20 Launchpad Translations on behalf of mdeslaur * po/it.po: Launchpad automatic translations update. 2012-05-19 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-05-18 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-05-17 Marc Deslauriers * NEWS: - updated NEWS for next release 2012-05-17 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.19 2012-05-17 Marc Deslauriers * NEWS: - added NEWS 2012-05-17 Marc Deslauriers * do-release: - Remove backup files in directories when doing a release 2012-05-17 Marc Deslauriers * NEWS: - prepare for next release 2012-05-17 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.18 2012-05-15 Marc Deslauriers * NEWS: * pasaffe_lib/Window.py: - Robert Ancell 2012-05-15 Disable Launchpad integration 2012-05-15 Robert Ancell * NEWS: * pasaffe_lib/Window.py: Disable Launchpad integration (no longer supported) 2012-05-13 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2012-05-07 Marc Deslauriers * NEWS: * README: * bin/pasaffe-import-keepassx: * pasaffe_lib/keepassx.py: - Can now import KeePass2 XML files (Thanks to Mathieu Trudel-Lapierre!) 2012-05-07 Mathieu Trudel-Lapierre * bin/pasaffe-import-keepassx: * pasaffe_lib/keepassx.py: Allow importing KeePass2 XML files as well. 2012-05-07 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-05-05 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-05-04 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-05-03 Marc Deslauriers * NEWS: - Prepare for next release 2012-05-03 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.17 2012-05-02 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-04-29 Marc Deslauriers * NEWS: * pasaffe/__init__.py: - Add more limited fix for gettext and optparse 2012-04-29 Marc Deslauriers * NEWS: * bin/pasaffe: * bin/pasaffe-dump-db: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: - Revert r259, as this was causing problems displaying unicode chars in the pasaffe main window. 2012-04-29 Marc Deslauriers * NEWS: * pasaffe_lib/gpassfile.py: - Properly strip gpass database padding 2012-04-29 Marc Deslauriers * NEWS: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * pasaffe_lib/figaroxml.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/keepassx.py: - Fix logging in pasaffe-import scripts 2012-04-24 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - Properly handle entries with no URL specified (LP: #980608) 2012-04-24 Marc Deslauriers * NEWS: * bin/pasaffe: * bin/pasaffe-dump-db: * bin/pasaffe-import-entry: * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-gpass: * bin/pasaffe-import-keepassx: * pasaffe/AboutPasaffeDialog.py: * pasaffe/EditDetailsDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: - Properly handle unicode chars with optparse (LP: #983210) 2012-04-18 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-04-17 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-04-14 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-04-11 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-04-07 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-03-30 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: * pasaffe/__init__.py: - Don't set database specified on the command line as default 2012-03-25 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-03-24 Marc Deslauriers * NEWS: - prepare NEWS file for next release 2012-03-24 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.16 2012-03-24 Marc Deslauriers * NEWS: * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: - merged translations 2012-03-23 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/es.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-03-24 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: * pasaffe_lib/Window.py: - deactivate display secrets button when not needed 2012-03-22 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - fix right-click context menu 2012-03-22 Marc Deslauriers * NEWS: Prepare NEWS for 0.16 2012-03-22 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.15 2012-03-22 Marc Deslauriers * NEWS: added to NEWS 2012-03-22 Marc Deslauriers * README: - Added details to README file 2012-03-22 Marc Deslauriers * NEWS: Updated NEWS file 2012-03-22 Marc Deslauriers * bin/pasaffe-import-figaroxml: * pasaffe_lib/figaroxml.py: - print warning about skipped fields in figaro importer - properly handle utf-8 in figaro xml file 2012-03-22 Marc Deslauriers * README: * bin/pasaffe-import-keepassx: * pasaffe_lib/keepassx.py: - Added warning about certain fields not being imported - Properly handle keepassx utf-8 2012-03-21 Marc Deslauriers * pasaffe_lib/keepassx.py: - Fix string issue in keepassx import 2012-03-18 Marc Deslauriers * AUTHORS: * NEWS: - Updated AUTHORS and NEWS files 2012-03-18 Marc Deslauriers * bin/pasaffe-import-figaroxml: * bin/pasaffe-import-keepassx: * pasaffe_lib/figaroxml.py: * pasaffe_lib/keepassx.py: - Clean up unused parameters 2012-03-17 Marc Deslauriers * NEWS: * bin/pasaffe-import-figaroxml: - Give proper error in figaro XML import script 2012-03-17 Marc Deslauriers * bin/pasaffe-import-keepassx: * pasaffe_lib/keepassx.py: * NEWS: * TODO: - Added KeePassX import script 2012-03-04 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2012-03-03 Launchpad Translations on behalf of mdeslaur * po/es.po: Launchpad automatic translations update. 2012-03-01 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-02-29 Marc Deslauriers * NEWS: Prepared NEWS file for next release 2012-02-29 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.14 2012-02-27 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-02-26 Marc Deslauriers * NEWS: Updated NEWS 2012-02-26 Marc Deslauriers * data/ui/EditDetailsDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/NewPasswordDialog.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/SaveChangesDialog.ui: Set proper window titles 2012-02-26 Marc Deslauriers * po/pasaffe.pot: - Updated po files 2012-02-26 Marc Deslauriers * data/ui/PasaffeWindow.ui: - Add tooltips 2012-02-26 Marc Deslauriers * pasaffe/PasaffeWindow.py: - Reset search results if search field changed before hitting buttons 2012-02-26 Marc Deslauriers * pasaffe/PasaffeWindow.py: - Scroll left window to selected item 2012-02-26 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - Add find toolbar button - Make find menu entry a checkmenuitem 2012-02-25 Marc Deslauriers * NEWS: * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - Implemented search functionality 2012-02-24 Marc Deslauriers * NEWS: * data/media/pasaffe.svg: Slightly improve appearance of main icon 2012-02-18 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2012-02-17 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2012-02-15 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-02-14 Marc Deslauriers * NEWS: - Updated NEWS file for next release 2012-02-14 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.13 2012-02-14 Marc Deslauriers * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Merged launchpad translations 2012-02-14 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-02-14 Marc Deslauriers * TODO: added new TODO item 2012-02-14 Marc Deslauriers * data/ui/LockScreenDialog.ui: * data/ui/lock_screen_dialog.xml: * pasaffe/LockScreenDialog.py: * pasaffe/PasaffeWindow.py: Reuse main password dialog instead of the lock screen one 2012-02-14 Marc Deslauriers * data/ui/PasaffeWindow.ui: Improve lock screen message 2012-02-13 Marc Deslauriers * NEWS: * TODO: * data/ui/LockScreenDialog.ui: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - Lock the main window instead of popping up an unexpected dialog when the lock timeout occurs 2012-02-13 Marc Deslauriers * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Merged translations 2012-02-13 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-02-13 Marc Deslauriers * data/ui/EditDetailsDialog.ui: Add a tooltip to the random password button 2012-02-12 Marc Deslauriers * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Merged translations 2012-02-05 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-02-12 Marc Deslauriers * NEWS: * README: * data/ui/EditDetailsDialog.ui: * pasaffe/EditDetailsDialog.py: Added a random password generation button 2012-02-04 Marc Deslauriers * NEWS: Prepared NEWS file for next release 2012-02-04 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.12 2012-02-04 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - Fix crasher when adding a new item 2012-02-04 Marc Deslauriers * pasaffe/PasaffeWindow.py: - Fix typo in settings name 2012-02-04 Marc Deslauriers * data/GConf: * data/GConf/gsettings: * data/glib-2.0: * data/glib-2.0/schemas: * setup.cfg: * setup.py: * data/GConf/gsettings/pasaffe.convert: * data/pasaffe.convert: * data/glib-2.0/schemas/apps.pasaffe.gschema.xml: * data/apps.pasaffe.gschema.xml: - Properly fix gsettings and gconf convert script locations 2012-02-04 Marc Deslauriers * data/GConf: * data/GConf/gsettings: * data/GConf/gsettings/pasaffe.convert: * data/glib-2.0: * data/glib-2.0/schemas: * data/glib-2.0/schemas/apps.pasaffe.gschema.xml: * data/apps.pasaffe.gschema.xml: * setup.cfg: - Move gsettings schema file to better location - Add a gconf migration file 2012-02-04 Marc Deslauriers * NEWS: * data/apps.pasaffe.gschema.xml: Add database path description to GSettings schema file 2012-02-04 Marc Deslauriers * pasaffe_lib/preferences.py: * NEWS: * bin/pasaffe: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: - Refactor GSettings code to use bindings 2012-02-04 Marc Deslauriers * NEWS: * README: - Updated requirements in README file 2012-02-04 Marc Deslauriers * AUTHORS: * data/ui/AboutPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: - Added 2012 to copyright notice 2012-02-04 Marc Deslauriers * NEWS: * data/ui/AboutPasaffeDialog.ui: Fix about dialog layout 2012-02-04 Marc Deslauriers * NEWS: * data/ui/EditDetailsDialog.ui: Fix initial size of notes field 2012-02-04 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - Fix click on URL functionality 2012-02-04 Marc Deslauriers * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: - merged translations 2012-02-02 Launchpad Translations on behalf of mdeslaur * po/de.po: * po/fr.po: * po/it.po: * po/nl.po: * po/ru.po: Launchpad automatic translations update. 2012-02-04 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - Get rid of traceback when quitting without a selection 2012-02-01 Marc Deslauriers * pasaffe/__init__.py: pasaffe/__init__.py: set default filename on first run 2012-02-01 Marc Deslauriers * pasaffe_lib/Window.py: pasaffe_lib/Window.py: fix launchpadlib integration 2012-02-01 Marc Deslauriers * setup.cfg: setup.cfg: install GSettings Schema file 2012-02-01 Marc Deslauriers * data/apps.pasaffe.gschema.xml: * po/ru.po: * data/pasaffe.schemas.in: * NEWS: * pasaffe/EditDetailsDialog.py: * pasaffe/LockScreenDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/helpers.py: * pasaffe_lib/preferences.py: - Merged Francesco Marella's PyGI and GSettings branch 2012-01-29 Francesco Marella * data/apps.pasaffe.gschema.xml: * data/pasaffe.schemas.in: * pasaffe/EditDetailsDialog.py: * pasaffe/LockScreenDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/SaveChangesDialog.py: * pasaffe/__init__.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/helpers.py: * pasaffe_lib/preferences.py: Port Pasaffe to pygi and GSettings. 2012-01-14 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-01-13 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-01-12 Launchpad Translations on behalf of mdeslaur * po/ru.po: Launchpad automatic translations update. 2012-01-11 Marc Deslauriers * NEWS: * TODO: - Added a TODO item 2012-01-04 Launchpad Translations on behalf of mdeslaur * po/de.po: Launchpad automatic translations update. 2012-01-03 Launchpad Translations on behalf of mdeslaur * po/de.po: Launchpad automatic translations update. 2011-12-14 Launchpad Translations on behalf of mdeslaur * po/it.po: Launchpad automatic translations update. 2011-12-13 Launchpad Translations on behalf of mdeslaur * po/it.po: Launchpad automatic translations update. 2011-12-12 Launchpad Translations on behalf of mdeslaur * po/fr.po: * po/it.po: * po/nl.po: Launchpad automatic translations update. 2011-12-11 Marc Deslauriers * NEWS: Prepared NEWS file for next release 2011-12-11 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.11 2011-12-11 Marc Deslauriers * NEWS: Updated NEWS file 2011-12-11 Marc Deslauriers * README: - Added a README file 2011-12-11 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2011-12-10 Marc Deslauriers * NEWS: - Updated NEWS file 2011-12-10 Marc Deslauriers * debian: * debian/changelog: * debian/compat: * debian/control: * debian/copyright: * debian/rules: * do-release: - Remove debian directory, and fix do-release. This should be in distribution packaging, not in the upstream tarball. 2011-12-10 Marc Deslauriers * pasaffe_lib/Window.py: - Re-enable launchpad integration, since we'll try and get this into Precise. 2011-12-10 Marc Deslauriers * help/fr: * help/fr/delete-entry.page: * help/fr/figures: * help/fr/figures/icon.png: * help/fr/index.page: * help/fr/new-entry.page: * help/fr/preferences.page: * help/fr/show-secrets.page: * help/C/delete-entry.page: * help/C/index.page: * help/C/new-entry.page: * help/C/preferences.page: * help/C/show-secrets.page: - Added french help file - Corrected email address in help files 2011-12-10 Marc Deslauriers * help/C/delete-entry.page: * help/C/new-entry.page: * help/C/show-secrets.page: * help/C/topic1.page: * NEWS: * TODO: * help/C/figures/icon.png: * help/C/index.page: * help/C/preferences.page: - Added help file with basic usage instructions 2011-10-04 Launchpad Translations on behalf of mdeslaur * po/fr.po: * po/it.po: * po/nl.po: Launchpad automatic translations update. 2011-10-03 Launchpad Translations on behalf of mdeslaur * po/fr.po: * po/it.po: * po/nl.po: Launchpad automatic translations update. 2011-10-02 Marc Deslauriers * NEWS: Prepared NEWS file for next release 2011-10-02 Marc Deslauriers * NEWS: * debian/changelog: * po/pasaffe.pot: * setup.py: release 0.10 2011-10-02 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: Don't indicate a save is required when a new entry is cancelled 2011-10-02 Marc Deslauriers * NEWS: * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - merged improvements from Francesco Marella - Updated NEWS and TODO files 2011-09-28 Francesco Marella * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Implement the duplication of entries through the clone action 2011-09-28 Francesco Marella * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Add Context menu to left pane entries 2011-09-28 Francesco Marella * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Replace Cut-Copy-Paste actions with Clone 2011-09-30 Launchpad Translations on behalf of mdeslaur * po/fr.po: * po/it.po: * po/nl.po: Launchpad automatic translations update. 2011-09-27 Marc Deslauriers * .quickly: * data/ui/AboutPasaffeDialog.ui: Added contributors to about dialog 2011-09-27 Marc Deslauriers * NEWS: * TODO: Updated NEWS and TODO 2011-09-27 Marc Deslauriers * data/pasaffe.schemas.in: * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: Implement option to automatically save changes. (LP: #856302) Thanks to Francesco Marella! 2011-09-27 Francesco Marella * data/pasaffe.schemas.in: * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: Implement automatically save changes (LP: #856302) 2011-09-22 Marc Deslauriers * TODO: Added save option to TODO 2011-09-21 Launchpad Translations on behalf of mdeslaur * po/it.po: * po/nl.po: Launchpad automatic translations update. 2011-09-20 Launchpad Translations on behalf of mdeslaur * po/it.po: * po/nl.po: Launchpad automatic translations update. 2011-09-20 Marc Deslauriers * bin/pasaffe-import-figaroxml: * pasaffe_lib/figaroxml.py: * AUTHORS: * NEWS: * debian/copyright: Merged pasaffe-import-figaroxml from Francesco Marella Updated copyright, AUTHORS and NEWS files 2011-09-20 Francesco Marella * bin/pasaffe-import-figaroxml: * pasaffe_lib/figaroxml.py: pasaffe-import-figaroxml script helps to migrate from fpm2 to pasaffe. Open fpm2, export all your passwords to XML file and use pasaffe-import-figaroxml to import them in pasaffe. 2011-09-18 Launchpad Translations on behalf of mdeslaur * po/nl.po: Launchpad automatic translations update. 2011-09-17 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2011-09-16 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2011-09-15 Marc Deslauriers * NEWS: - Prepared NEWS file for next release 2011-09-15 Marc Deslauriers * NEWS: * debian/changelog: * po/pasaffe.pot: * setup.py: release 0.9 2011-09-15 Marc Deslauriers * TODO: - added a TODO 2011-09-10 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - Don't crash when adding a new entry and attempting to update the right pane when nothing is selected. 2011-08-29 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2011-08-28 Marc Deslauriers * NEWS: - Prepare NEWS file for new release 2011-08-28 Marc Deslauriers * NEWS: * debian/changelog: * po/pasaffe.pot: * setup.py: release 0.8 2011-08-28 Marc Deslauriers * NEWS: - Updated NEWS 2011-08-28 Marc Deslauriers * setup.py: - python-distutils-extra in Lucid and Maverick doesn't support Mallard help files, so add workaround in setup.py 2011-08-26 Marc Deslauriers * bin/pasaffe-dump-db: * AUTHORS: * NEWS: * debian/copyright: - merged pasaffe-dump-db thanks to Jamie Standboge - updated AUTHORS and debian/copyright files 2011-08-26 Jamie Strandboge * bin/pasaffe-dump-db: * NEWS: add bin/pasaffe-dump-db 2011-08-26 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2011-08-25 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: - Only update right pane if the same entry is still selected 2011-08-25 Marc Deslauriers * NEWS: * debian/changelog: * po/pasaffe.pot: * setup.py: release 0.7 2011-08-25 Marc Deslauriers * po/fr.po: - merge translations 2011-08-25 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2011-08-25 Marc Deslauriers * NEWS: * pasaffe/PasaffeWindow.py: Prevent the edit entry dialog from opening twice 2011-08-24 Marc Deslauriers * po/fr.po: - Merge translations from launchpad 2011-08-24 Launchpad Translations on behalf of mdeslaur * po/fr.po: Launchpad automatic translations update. 2011-08-24 Marc Deslauriers * bin/pasaffe-import-entry: * NEWS: - Added script to import an entry from the command line 2011-08-23 Marc Deslauriers * NEWS: - Update NEWS 2011-08-23 Marc Deslauriers * pasaffe_lib/preferences.py: - better handle empty gconf keys 2011-08-23 Marc Deslauriers * NEWS: - Add new section to NEWS 2011-08-23 Marc Deslauriers * do-release: - also create new release in debian/changes 2011-08-23 Marc Deslauriers * debian/changelog: New release 2011-08-23 Marc Deslauriers * NEWS: * po/pasaffe.pot: * setup.py: release 0.6 2011-08-23 Marc Deslauriers * NEWS: Added NEWS 2011-08-23 Marc Deslauriers * data/ui/AboutPasaffeDialog.ui: * data/ui/EditDetailsDialog.ui: * data/ui/LockScreenDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/NewPasswordDialog.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/PreferencesPasaffeDialog.ui: * data/ui/SaveChangesDialog.ui: - fixed compatibility with recent glade 2011-08-23 Marc Deslauriers * NEWS: * TODO: - Updated NEWS and TODO 2011-08-23 Marc Deslauriers * po/fr.po: - Updated french translation 2011-08-23 Marc Deslauriers * po/pasaffe.pot: Update .pot file 2011-08-23 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe/__init__.py: Make more strings translatable 2011-08-23 Marc Deslauriers * pasaffe_lib/Window.py: - pasaffe_lib/Window.py: use new preference name 2011-08-23 Marc Deslauriers * po/fr.po: - add the start of a french translation 2011-08-23 Marc Deslauriers * po: * po/pasaffe.pot: * NEWS: * do-release: - uncomment bzr stuff in do-release - added po directory - added NEWS 2011-08-23 Marc Deslauriers * do-release: * NEWS: * setup.py: - Added do-release maintainer script based on the one by Martin Pitt in the jockey package - Make setup.py executable - Formatted NEWS file to work with do-release 2011-08-23 Marc Deslauriers * pasaffe_lib/pasaffeconfig.py: pasaffe_lib/pasaffeconfig.py: set version to 0. It automatically gets updated by setup.py. 2011-08-22 Marc Deslauriers * NEWS: * data/ui/PasaffeWindow.ui: Added missing Open URL button tooltip 2011-08-22 Marc Deslauriers * NEWS: * debian/changelog: * pasaffe_lib/pasaffeconfig.py: * setup.py: New release 2011-08-22 Marc Deslauriers * NEWS: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - Added toolbar button and keyboard shortcut to open URL 2011-08-22 Marc Deslauriers * NEWS: * data/pasaffe.schemas.in: * data/ui/PasaffeWindow.ui: * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: - Change secrets logic 2011-08-21 Marc Deslauriers * NEWS: * debian/changelog: * pasaffe_lib/pasaffeconfig.py: * setup.py: New release 2011-08-21 Marc Deslauriers * NEWS: - Updated NEWS file 2011-08-21 Marc Deslauriers * TODO: - This should work on 10.04 now, remove item from TODO 2011-08-21 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/gpassfile.py: * pasaffe_lib/readdb.py: - Get rid of some warnings 2011-08-21 Marc Deslauriers * pasaffe_lib/Builder.py: - Use gobject instead of gi.repository for Lucid compatibility 2011-08-21 Marc Deslauriers * NEWS: - Updated NEWS file 2011-08-21 Marc Deslauriers * TODO: * pasaffe/PasaffeWindow.py: - Implement confirmation dialog when entry is deleted 2011-08-21 Marc Deslauriers * TODO: * pasaffe/PasaffeWindow.py: - Use case-insensitive sort in left pane 2011-08-21 Marc Deslauriers * TODO: Add some more TODOs 2011-08-20 Marc Deslauriers * TODO: - Added some TODOs 2011-08-20 Marc Deslauriers * NEWS: - Added a NEWS file 2011-08-20 Marc Deslauriers * pasaffe_lib/Window.py: - pasaffe_lib/Window.py: disable launchpad integration since this isn't being shipped in Ubuntu. 2011-08-20 Marc Deslauriers * debian/changelog: * pasaffe_lib/pasaffeconfig.py: * setup.py: New release 2011-08-20 Marc Deslauriers * data/pasaffe.schemas.in: * setup.cfg: Added a gconf schema file 2011-08-20 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - Add URL open to menu, and display URLs as links 2011-08-20 Marc Deslauriers * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: - Add option to make notes be considered secret. With this option checked, notes will be hidden when the password is hidden. 2011-08-20 Marc Deslauriers * TODO: update TODO 2011-08-20 Marc Deslauriers * pasaffe/PasaffeWindow.py: - Make window title reflect save status 2011-08-20 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - Make menu item a toggle also, and sync them 2011-08-20 Marc Deslauriers * .quickly: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - make show password button a toggle 2011-08-20 Marc Deslauriers * pasaffe/PasaffeWindow.py: Fix dict/list typo 2011-08-19 Marc Deslauriers * TODO: Added stuff to TODO 2011-08-17 Marc Deslauriers * debian/changelog: * pasaffe_lib/pasaffeconfig.py: * setup.py: New release. 2011-08-17 Marc Deslauriers * pasaffe_lib/readdb.py: - pasaffe_lib/readdb.py: fix problem starting from unity launcher because of use of os.getlogin() 2011-07-29 Marc Deslauriers * pasaffe/EditDetailsDialog.py: * pasaffe/LockScreenDialog.py: * pasaffe/NewDatabaseDialog.py: * pasaffe/NewPasswordDialog.py: * pasaffe/PasswordEntryDialog.py: * pasaffe/SaveChangesDialog.py: Fix inadvertent license snafu 2011-07-27 Marc Deslauriers * data/ui/AboutPasaffeDialog.ui: * pasaffe/AboutPasaffeDialog.py: - Add version number to about dialog - Improve license in about dialog 2011-07-27 Marc Deslauriers * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/readdb.py: Use version from pasaffeconfig.py in database 2011-07-27 Marc Deslauriers * setup.py: Fill out some info in setup.py 2011-07-26 Marc Deslauriers * TODO: Update TODO list 2011-07-26 Marc Deslauriers * data/ui/NewPasswordDialog.ui: * data/ui/PasaffeWindow.ui: Use "master password" when referring to the password that protects the database. 2011-07-26 Marc Deslauriers * pasaffe/PasaffeWindow.py: Don't copy to PRIMARY when selected from menu 2011-07-26 Marc Deslauriers * pasaffe/PasaffeWindow.py: also copy to PRIMARY clipboard 2011-07-26 Marc Deslauriers * pasaffe.desktop.in: Better description in desktop file 2011-07-26 Marc Deslauriers * TODO: - added some TODOs 2011-07-23 Marc Deslauriers * TODO: * bin/pasaffe-import-gpass: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - Implement saving to temp file 2011-07-23 Marc Deslauriers * pasaffe_lib/readdb.py: readdb.py: move stuff around so we can have more than one instance 2011-07-19 Marc Deslauriers * TODO: Updated TODO file 2011-07-19 Marc Deslauriers * pasaffe_lib/preferences.py: * setup.py: Move gconf stuff out of __init__ so setup.py works 2011-07-19 Marc Deslauriers * debian/control: * debian/copyright: - more packaging work 2011-07-19 Marc Deslauriers * debian: * debian/changelog: * debian/compat: * debian/control: * debian/copyright: * debian/rules: Creating ubuntu package 2011-07-16 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - Add information dialog - Correctly remove deprecated username field 2011-07-16 Marc Deslauriers * TODO: Added stuff to TODO 2011-07-16 Marc Deslauriers * data/ui/NewPasswordDialog.ui: * data/ui/new_password_dialog.xml: * pasaffe/NewPasswordDialog.py: * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Added change password dialog and menu item 2011-07-16 Marc Deslauriers * TODO: updated TODO 2011-07-16 Marc Deslauriers * data/ui/LockScreenDialog.ui: * data/ui/lock_screen_dialog.xml: * pasaffe/LockScreenDialog.py: - added new lock screen files 2011-07-16 Marc Deslauriers * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: - added inactivity timeout for lock screen - added idle settings to preferences dialog 2011-07-16 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: Added lock screen 2011-07-15 Marc Deslauriers * TODO: * pasaffe_lib/readdb.py: - add more fields to database header on save - bump database version to 0x302 2011-07-15 Marc Deslauriers * TODO: * pasaffe_lib/readdb.py: Generate new database keys on each save 2011-07-15 Marc Deslauriers * pasaffe/PasaffeWindow.py: Don't fail if an entry doesn't have one of the timestamps 2011-07-15 Marc Deslauriers * pasaffe_lib/gpassfile.py: passafe_lib/gpassfile.py: fix problem when there is no padding to remove 2011-07-15 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe/__init__.py: - make database path configurable in gconf and via command line 2011-07-15 Marc Deslauriers * pasaffe_lib/Window.py: * pasaffe_lib/preferences.py: Hook up gconf callback 2011-07-14 Marc Deslauriers * TODO: * data/ui/PreferencesPasaffeDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/preferences.py: - Added gconf preference backend - Added visible passwords as a preference 2011-07-14 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Add display password item to menu 2011-07-14 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Add copy URL item to menu 2011-07-14 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: Clarified toolbar tooltips Switch toolbar icons 2011-07-14 Marc Deslauriers * TODO: More TODOs 2011-07-14 Marc Deslauriers * TODO: More TODOs 2011-07-14 Marc Deslauriers * TODO: Added some TODOs 2011-07-13 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - add display password button, and hide passwords by default 2011-07-13 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - added copy button to toolbar 2011-07-13 Marc Deslauriers * TODO: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Implement copy and paste of username, password and text 2011-07-12 Marc Deslauriers * TODO: Added another TODO 2011-07-12 Marc Deslauriers * TODO: Added TODO file with list of things to do 2011-07-11 Marc Deslauriers * bin/pasaffe-import-gpass: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: Restrict permissions on new databases Handle missing databases when importing from gpass 2011-07-11 Marc Deslauriers * bin/pasaffe-import-gpass: bin/pasaffe-import-gpass: update for changes to password use 2011-07-11 Marc Deslauriers * pasaffe/PasaffeWindow.py: Make sure mainloop is running before calling gtk.main_quit() 2011-07-11 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: Don't keep password and stretched pass around longer than necessary 2011-07-10 Marc Deslauriers * pasaffe_lib/gpassfile.py: * pasaffe_lib/readdb.py: code cleanup: switch to proper logging facilities 2011-07-10 Marc Deslauriers * pasaffe/PasaffeWindow.py: - Fix problem when deleting an entry - When adding a new entry, add it in the sorted list and default to it 2011-07-10 Marc Deslauriers * pasaffe/PasaffeWindow.py: - fixed bug when editing URL - sort entries in main window by default 2011-07-09 Marc Deslauriers * data/ui/EditDetailsDialog.ui: Add scrollbars to notes entry field 2011-07-09 Marc Deslauriers * data/media/background.png: * data/media/pasaffe.svg: - removed unused background.png - added new icon first draft 2011-07-09 Marc Deslauriers * bin/pasaffe-import-gpass: * pasaffe/PasaffeWindow.py: * pasaffe_lib/gpassfile.py: improve handling of empty URL and notes improve warning in gpass importation tool 2011-07-08 Marc Deslauriers * bin/pasaffe-import-gpass: * pasaffe_lib/blowfish.py: * pasaffe_lib/gpassfile.py: * AUTHORS: Added tool to import GPass password databases 2011-07-08 Marc Deslauriers * pasaffe_lib/pytwofishcbc.py: * AUTHORS: * pasaffe_lib/readdb.py: code cleanup: implement CBC into it's own class 2011-07-07 Marc Deslauriers * data/ui/EditDetailsDialog.ui: * data/ui/PasaffeWindow.ui: added tooltips to main window and entry edit window 2011-07-07 Marc Deslauriers * pasaffe/__init__.py: * pasaffe_lib/Window.py: * pasaffe_lib/preferences.py: Comment out couchdb preferences backend for now 2011-07-07 Marc Deslauriers * pasaffe/PasaffeWindow.py: hex encode uuid in left pane 2011-07-06 Marc Deslauriers * pasaffe_lib/readdb.py: Fix bug when writing out long fields to DB 2011-07-06 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Greatly enhance layout of right pane 2011-07-06 Marc Deslauriers * data/ui/EditDetailsDialog.ui: * pasaffe/PasaffeWindow.py: Add URL field Make notes field bigger Reorder entries 2011-07-06 Marc Deslauriers * data/ui/EditDetailsDialog.ui: * data/ui/NewDatabaseDialog.ui: * data/ui/PasswordEntryDialog.ui: * data/ui/SaveChangesDialog.ui: Improve appearance of dialogs 2011-07-06 Marc Deslauriers * data/ui/SaveChangesDialog.ui: * data/ui/save_changes_dialog.xml: * pasaffe/SaveChangesDialog.py: * pasaffe/PasaffeWindow.py: Added save warning dialog on program exit 2011-07-05 Marc Deslauriers * examples/test.psafe3: * examples/pasaffe.psafe3: - renamed example database 2011-07-05 Marc Deslauriers * data/ui/NewDatabaseDialog.ui: * data/ui/new_database_dialog.xml: * pasaffe/NewDatabaseDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: Implement new database dialog 2011-07-04 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - implement new db formatting - exit properly 2011-07-04 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - implement some menu items 2011-07-04 Marc Deslauriers * data/ui/PasaffeWindow.ui: - Removed status bar - Removed unneeded menu entries - Added "Add" menu entry 2011-07-04 Marc Deslauriers * data/ui/PasswordEntryDialog.ui: * pasaffe/PasaffeWindow.py: - reset dialog when wrong password is entered 2011-07-04 Marc Deslauriers * data/ui/PasswordEntryDialog.ui: * pasaffe/PasaffeWindow.py: * pasaffe_lib/readdb.py: - Implement password validation - Implement exceptions in readdb backend 2011-07-04 Marc Deslauriers * data/ui/PasswordEntryDialog.ui: * data/ui/password_entry_dialog.xml: * pasaffe/PasswordEntryDialog.py: Added password dialog 2011-07-04 Marc Deslauriers * pasaffe/PasaffeWindow.py: - Update timestamps when entry is modified 2011-07-04 Marc Deslauriers * pasaffe/PasaffeWindow.py: * pasaffe/__init__.py: * pasaffe_lib/readdb.py: - implement saving 2011-07-04 Marc Deslauriers * pasaffe/PasaffeWindow.py: - added last modification time to main window 2011-07-03 Marc Deslauriers * pasaffe/PasaffeWindow.py: - implement add, edit, and remove toolbar button functionnality 2011-07-03 Marc Deslauriers * data/ui/PasaffeWindow.ui: - add more buttons to toolbar 2011-07-03 Marc Deslauriers * pasaffe/PasaffeWindow.py: - change name in tree if modified - implement needs saving flag 2011-07-03 Marc Deslauriers * data/ui/EditDetailsDialog.ui: * data/ui/edit_details_dialog.xml: * pasaffe/EditDetailsDialog.py: * pasaffe/PasaffeWindow.py: - added details edit dialog 2011-07-03 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Use UUID when looking up records 2011-07-03 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - implement save toolbar callback 2011-07-03 Marc Deslauriers * data/ui/PasaffeWindow.ui: text widget shouldn't be editable 2011-07-03 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Further work on data display 2011-07-03 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Start implementing data display 2011-07-03 Marc Deslauriers * examples: * examples/README: * examples/test.psafe3: - added example password safe database file for development purposes 2011-07-03 Marc Deslauriers * docs: * docs/formatV3.txt: * AUTHORS: * COPYING: - added password safe database format documentation 2011-07-03 Marc Deslauriers * pasaffe_lib/pytwofish.py: * pasaffe_lib/readdb.py: * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: - add library that reads and writes password safe files 2011-07-03 Marc Deslauriers * data/ui/PasaffeWindow.ui: * pasaffe/PasaffeWindow.py: Add stuff to left pane 2011-07-01 Marc Deslauriers * help/C/index.page: * help/C/preferences.page: * help/C/topic1.page: - Added copyright info to help pages 2011-07-01 Marc Deslauriers * data/ui/PasaffeWindow.ui: - set horizontal window division to a sane default 2011-07-01 Marc Deslauriers * COPYING: * data/ui/PasaffeWindow.ui: - added COPYING file - Set default window size 2011-07-01 Marc Deslauriers * AUTHORS: * bin/pasaffe: * data/ui/AboutPasaffeDialog.ui: * pasaffe/AboutPasaffeDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/preferences.py: * setup.py: * tests/test_example.py: * tests/test_lint.py: - Set license to GPL-3 2011-07-01 Marc Deslauriers * data/ui/PasaffeWindow.ui: - added split panes to main window 2011-06-30 Marc Deslauriers * .quickly: * AUTHORS: * bin: * bin/pasaffe: * data: * data/media: * data/media/background.png: * data/media/pasaffe.svg: * data/ui: * data/ui/AboutPasaffeDialog.ui: * data/ui/PasaffeWindow.ui: * data/ui/PreferencesPasaffeDialog.ui: * data/ui/about_pasaffe_dialog.xml: * data/ui/pasaffe_window.xml: * data/ui/preferences_pasaffe_dialog.xml: * help: * help/C: * help/C/figures: * help/C/figures/icon.png: * help/C/index.page: * help/C/preferences.page: * help/C/topic1.page: * pasaffe: * pasaffe.desktop.in: * pasaffe/AboutPasaffeDialog.py: * pasaffe/PasaffeWindow.py: * pasaffe/PreferencesPasaffeDialog.py: * pasaffe/__init__.py: * pasaffe_lib: * pasaffe_lib/AboutDialog.py: * pasaffe_lib/Builder.py: * pasaffe_lib/PreferencesDialog.py: * pasaffe_lib/Window.py: * pasaffe_lib/__init__.py: * pasaffe_lib/helpers.py: * pasaffe_lib/pasaffeconfig.py: * pasaffe_lib/preferences.py: * setup.py: * tests: * tests/test_example.py: * tests/test_lint.py: Initial project creation with Quickly! ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/MANIFEST0000664000175000017500000000575000000000000015667 0ustar00mdeslaurmdeslaur00000000000000# file GENERATED by distutils, do NOT edit .quickly AUTHORS COPYING ChangeLog MANIFEST NEWS README README.development TODO do-release pasaffe.desktop.in setup.py bin/pasaffe bin/pasaffe-cli bin/pasaffe-dump-db bin/pasaffe-import-entry bin/pasaffe-import-figaroxml bin/pasaffe-import-gpass bin/pasaffe-import-keepassx data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml data/icons/scalable/apps/pasaffe.svg data/ui/AboutPasaffeDialog.ui data/ui/EditDetailsDialog.ui data/ui/EditFolderDialog.ui data/ui/NewDatabaseDialog.ui data/ui/NewPasswordDialog.ui data/ui/PasaffeWindow.ui data/ui/PasswordEntryDialog.ui data/ui/PreferencesPasaffeDialog.ui data/ui/SaveChangesDialog.ui data/ui/about_pasaffe_dialog.xml data/ui/edit_details_dialog.xml data/ui/edit_folder_dialog.xml data/ui/new_database_dialog.xml data/ui/new_password_dialog.xml data/ui/pasaffe_window.xml data/ui/password_entry_dialog.xml data/ui/preferences_pasaffe_dialog.xml data/ui/save_changes_dialog.xml docs/formatV3.txt docs/formatV322.txt docs/interopability.txt help/C/delete-entry.page help/C/index.page help/C/legal.xml help/C/new-entry.page help/C/preferences.page help/C/show-secrets.page help/C/figures/icon.png help/fr/delete-entry.page help/fr/index.page help/fr/legal.xml help/fr/new-entry.page help/fr/preferences.page help/fr/show-secrets.page help/fr/figures/icon.png mime/pasaffe.xml pasaffe/AboutPasaffeDialog.py pasaffe/EditDetailsDialog.py pasaffe/EditFolderDialog.py pasaffe/NewDatabaseDialog.py pasaffe/NewPasswordDialog.py pasaffe/PasaffeWindow.py pasaffe/PasswordEntryDialog.py pasaffe/PreferencesPasaffeDialog.py pasaffe/SaveChangesDialog.py pasaffe/__init__.py pasaffe_lib/AboutDialog.py pasaffe_lib/Builder.py pasaffe_lib/PreferencesDialog.py pasaffe_lib/Window.py pasaffe_lib/__init__.py pasaffe_lib/blowfish.py pasaffe_lib/figaroxml.py pasaffe_lib/gpassfile.py pasaffe_lib/helpers.py pasaffe_lib/helpersgui.py pasaffe_lib/keepassx.py pasaffe_lib/pasaffeconfig.py pasaffe_lib/pytwofish.py pasaffe_lib/pytwofishcbc.py pasaffe_lib/readdb.py po/de.po po/en_GB.po po/es.po po/fr.po po/it.po po/ko.po po/nl.po po/pasaffe.pot po/ru.po tests/generate_pasaffe_db.py tests/test_dump_db.py tests/test_figaro_079.py tests/test_figaro_import.py tests/test_gpass_050.py tests/test_gpass_import.py tests/test_helpers.py tests/test_import_entry.py tests/test_keepass2_224.py tests/test_keepass2_import.py tests/test_keepassx_043.py tests/test_keepassx_import.py tests/test_lint.py tests/test_pasaffe-cli.py tests/test_pasaffe_025.py tests/test_passwdsafe_510.py tests/test_pw_gorilla_15363.py tests/test_pw_gorilla_1537.py tests/test_pwsafe_331.py tests/test_readdb.py tests/databases/README tests/databases/figaro-079.xml tests/databases/gpass-050.gps tests/databases/keepass2-224.kdbx tests/databases/keepass2-224.xml tests/databases/keepassx-043.kdb tests/databases/keepassx-043.xml tests/databases/pasaffe-025.psafe3 tests/databases/passwdsafe-510.psafe3 tests/databases/pw-gorilla-15363.psafe3 tests/databases/pw-gorilla-1537.psafe3 tests/databases/pwsafe-331.psafe3 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702281.5000415 pasaffe-0.57/NEWS0000644000175000017500000003204200000000000015225 0ustar00mdeslaurmdeslaur00000000000000This file summarizes the major and interesting changes for each release. For a detailed list of changes, please see ChangeLog. 0.57 (2020-12-11) * Enhancements: * Bug fixes: - Fixed deprecated ElementTree API (LP: #1907304) - Fix some deprecation warnings in the test suite 0.56 (2020-01-28) * Enhancements: * Bug fixes: - Fix some warnings with a newer version of flake8 (LP: #1860613) 0.55 (2019-11-15) * Enhancements: - Passwords are now displayed in a monospaced font in the right pane. * Bug fixes: - PEP 8 cleanup 0.54 (2019-10-23) * Enhancements: - Clear the clipboard when locking or closing the window. Do not clear clipboard content from another program. - Make Double-click behaviour configurable. - Make displaying usernames in tree configurable. - Password field is now displayed in a monospaced font to prevent certain characters from looking the same. * Bug fixes: 0.53 (2019-09-22) * Enhancements: - Double click on tree item toggles folder expanded state or copies password. - On enter, toggles folder expanded state or opens password entry for edit. - Navigate tree with left & right key presses: toggling expanded state or going to parent / child. * Bug fixes: - Fixed bug in set_tree_expansion which could result in only partial expansion of the tree. - goto_uuid now also correctly displays folders. 0.52 (2019-09-19) * Enhancements: - Show copy menu items first in the tree item context menu - Display username in the tree view item title * Bug fixes: - Fix copy truncates password containing pound sign (LP: #1819141) 0.51 (2017-12-20) * Enhancements: - Switch folder icon to symbolic icon - Updated translations * Bug fixes: 0.50 (2017-12-19) * Enhancements: - Set window title based on current loaded file. Thanks to Tristan Hill. - Switch to symbolic icons - Updated translations * Bug fixes: - Make password window non-resizable - Escape text e.g. in case of & in entry name. Thanks to Tristan Hill. - Fix issue where Pasaffe would think a save was required when no fields were changed in an entry. - Fix some untranslated strings. (LP: #1691668) - Updated deprecated Gtk widgets. - Properly support popup menus on Wayland. 0.49 (2017-05-17) * Enhancements: - Updated translations - Add support for opening psafe3 files from the file manager. Thanks to Tristan Hill. * Bug fixes: - Raised password iteration limit from 50k to 100k. Thanks to Joshua C. Randall. (LP: #1689784) 0.48 (2017-03-13) * Enhancements: * Bug fixes: - Fixed compatibility with GTK+ 3.20. Thanks to Mathieu Trudel-Lapierre for reporting the issue and for the initial patch. 0.47 (2016-06-16) * Enhancements: - Updated translations * Bug fixes: - Error when opening database containing missing entry titles (LP: #1586335) - Properly handle blank entry and folder titles - Don't crash if yelp is not installed 0.46 (2016-03-13) * Enhancements: * Bug fixes: - Searches are now accent-insensitive (LP: #1488333) - PEP 8 cleanup 0.45 (2016-02-17) * Enhancements: - Added new pasaffe-cli command-line interface, thanks to C de-Avillez. * Bug fixes: - Specify Gtk version to prevent warning - Stop using deprecated find icon 0.44 (2015-07-30) * Enhancements: * Bug fixes: - PEP 8 cleanup - No longer hardcode icon location (LP: #1459823) 0.43 (2015-03-08) * Enhancements: - Updated translations * Bug fixes: - Make sure left pane is focused on startup 0.42 (2015-02-05) * Enhancements: - Updated translations - Slightly improve preferences dialog layout * Bug fixes: - Fix more GTK deprecations 0.41 (2014-11-03) * Enhancements: - Updated translations * Bug fixes: - Fixed a regression in 0.40 that added extra lines in notes at each save 0.40 (2014-11-02) * Enhancements: - Updated translations - Store notes field with CRLF in the database for interopability reasons - Add a raw dump option to pasaffe-dump-db * Bug fixes: - Properly handle databases that don't contain username or password fields (LP: #1384710) - Make sure all dialogs have transient parents (LP: #1386064) - Stop using deprecated Gtk stock items (LP: #1386064) - Disable menu and toolbar entries for missing fields 0.39 (2014-07-31) * Enhancements: - Improve random password generation - Improve layout of database information window - Display database file location in information window - Corrected title in new entry and new folder windows (LP: #1314950) - Edit button in toolbar now also edits folders (LP: #1350294) * Bug fixes: - Fixed [icon] text in help file, and license text. (LP: #1314956) 0.38 (2014-04-04) * Enhancements: - Updated translations * Bug fixes: - Added some missing translations (LP: #1300012) 0.37 (2014-02-15) * Enhancements: - Import scripts now handle folders - Updated translations * Bug fixes: - Fixed KeePass2 import script and added tests 0.36 (2014-02-12) * Enhancements: - Improved right pane layout - Updated translations * Bug fixes: 0.35 (2014-02-09) * Enhancements: - Migrated to Python 3 - Added translator credits to about dialog (LP: #1273673) - Updated translations * Bug fixes: 0.34 (2014-01-18) * Enhancements: - Updated translations * Bug fixes: - Fix new PyGI deprecations 0.33 (2013-10-02) * Enhancements: - Updated translations * Bug fixes: - Don't crash when search string ends with a backslash (LP: #1231744) 0.32 (2013-09-26) * Enhancements: * Bug fixes: - Allow search to work on entries in collapsed folders 0.31 (2013-09-26) * Enhancements: * Bug fixes: - Don't crash when attempting to add an entry with nothing selected 0.30 (2013-09-08) * Enhancements: - Updated translations * Bug fixes: - Fix help file location when using python-distutils-extra >= 2.38 0.29 (2013-09-04) * Enhancements: - Implemented folder expansion saving to database - Implemented folder expansion preferences - Folder window size is now saved - Updated translations * Bug fixes: - Set icon column sizing to automatic so tree doesn't move around 0.28 (2013-08-15) * Enhancements: * Bug fixes: - Fix some translation mistakes in the fr help file (LP: #1210552) - Fix crash when changing the show secrets setting - Mark some more menu entries as translatable - Perform case-insensitive sort on entries and folders 0.27 (2013-08-12) * Enhancements: - Implemented drag and drop of folders and entries - Fix broken menu entries when gtk is configured to show icons in menus - Deactivate certain menu items when a folder is selected * Bug fixes: 0.26 (2013-08-11) * Enhancements: * Bug fixes: - Folder text entry now activates OK button (thanks jdstrand!) - Tree now sorts folders before directories (thanks jdstrand!) - Fixed typeahead find (thanks hallyn!) 0.25 (2013-08-09) * Enhancements: - Added tree support (Thanks to Jabik Postmus!) - Improved test suite - Updated translations * Bug fixes: - Fixed some untranslated strings (LP: #1202980) 0.24 (2013-03-06) * Enhancements: - Updated translations * Bug fixes: - Fix pylint warnings and test suite - Removed deprecated GObject usage 0.23 (2013-02-12) * Enhancements: * Bug fixes: - Fixed adding new entries 0.22 (2013-02-09) * Enhancements: - Updated translations * Bug fixes: - Command line tools can now be run without a GUI 0.21 (2012-08-21) * Enhancements: - Pasaffe now remembers window sizes * Bug fixes: 0.20 (2012-08-14): * Enhancements: - Made password length configurable (Thanks to Mathieu Trudel-Lapierre!) - Automatically clears clipboard after 20 seconds * Bug fixes: - Moved gsettings schema from apps.pasaffe to net.launchpad.pasaffe 0.19 (2012-05-17): * Enhancements: * Bug fixes: - Fix do-release script to remove backup files before creating release tarball. 0.18 (2012-05-17): * Enhancements: - Can now import KeePass2 XML files (Thanks to Mathieu Trudel-Lapierre!) - Disable Launchpad integration (no longer supported) * Bug fixes: 0.17 (2012-05-03): * Enhancements: - Add command-line option to specify default database * Bug fixes: - Don't save database specified on command line as default - Properly handle entries that have no URL specified (LP: #980608) - Fix logging in pasaffe-import scripts so we can debug failures (LP: #991143) - Correctly strip gpass database padding (LP: #991204) - Properly handle unicode characters with optparse (LP: #983210) 0.16 (2012-03-24): * Enhancements: - Updated translations * Bug fixes: - Fix right click context menu (LP: #945861) - Deactivate "Display secrets" button when not needed 0.15 (2012-03-22): * Enhancements: - Updated translations - Added KeePassX XML import script - Import scripts now warn about skipped fields * Bug fixes: - Give proper error when not specifying file to Figaro import script - Various cleanups - Properly handle utf-8 characters when importing Figaro xml file 0.14 (2012-02-29): * Enhancements: - Updated translations - Slightly improved appearance of main icon - Implemented search * Bug fixes: - Set proper window titles 0.13 (2012-02-14): * Enhancements: - Added random password generation - Lock now uses the main window to prevent a window from showing up on another workspace when the timeout occurs * Bug fixes: 0.12 (2012-02-04): * Enhancements: - Updated translations - Added a TODO item - Migrated to PyGObject and GSettings, thanks to Francesco Marella * Bug fixes (related to PyGObject and GSettings migration): - Get rid of traceback when quitting without a selection - Restore click on URL functionality - Fix initial size of notes field - Fix about dialog layout - Updated requirements in README file - Refactor and simplify GSettings code to use bindings - Add database path description to GSettings schema file - Add GConf to GSettings migration file - Fix crasher when sometimes adding a new entry 0.11 (2011-12-11): * Enhancements: - A help file is now available with basic usage instructions. - Re-enable launchpad integration - Added a README file * Bug fixes: - Removed debian packaging files from upstream project 0.10 (2011-10-02): * Enhancements: - Numerous improvements, thanks to Francesco Marella: - Figaro xml import script - Added option to automatically save changes - Added context menu to left pane - Added Clone command to clone an entry * Bug fixes: - Save menu option and toolbar icon are now inactive when there is nothing to save. - Don't indicate save is required when a new entry is cancelled 0.9 (2011-09-15): * Enhancements: * Bug fixes: - Don't crash if a new entry is added and no entry is currently selected when attempting to update the right pane. 0.8 (2011-08-28): * Enhancements: - add pasaffe-dump-db to dump contents of DB to stdout in a human readable format * Bug fixes: - Only update right pane after editing an entry if it's still the one that is currently selected - Properly install Mallard help files in Lucid and Maverick 0.7 (2011-08-25): * Enhancements: - Added a simple script to import an entry from the command line * Bug fixes: - Correctly set default preferences - Prevent the edit entry dialog from opening twice and being confused 0.6 (2011-08-23): * Enhancements: - Added some maintainer scripts - Added po directory and translation file - Added french translation * Bug fixes: - Fixed missing tooltip on Open URL button - Fixed missing string translations - Fixed compatibility with newer glade 0.5 (20110822): * Enhancements: - Option to treat either all data or just passwords as secrets - Rename options and button labels - Added toolbar button and keyboard shortcut to open URL * Bug fixes: - None 0.4 (20110821): * Enhancements: - Now has a NEWS file that lists changes - Entry list is now uses a case-insensitive sort - A confirmation dialog has been added when deleting an entry - Now compatible with Ubuntu 10.04 * Bug fixes: - Disabled Launchpad integration since this isn't packaged in Ubuntu. - Removed some warnings when using time.time() 0.3 (20110820): * Enhancements: - Notes can now be configured to be secret, and they will be hidden when the passwords are hidden. - Window title will now indicate if the database has been modified and needs to be saved. - The "Display Password" toolbar button and menu item can now be toggled on and off for an entry. * Bug fixes: - Ship a proper GConf schema - Fix list being improperly created as a dict in the clipboard code 0.2 (20110817): * Enhancements: - About window now shows version number - Copy will also go to PRIMARY clipboard - Improved some wording in dialogs and desktop file - etc. * Bug fixes: - Fix problem starting properly from launchers 0.1 (20110723): * Initial release ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/PKG-INFO0000664000175000017500000000104100000000000015620 0ustar00mdeslaurmdeslaur00000000000000Metadata-Version: 1.1 Name: pasaffe Version: 0.57 Summary: Password manager for GNOME Home-page: https://launchpad.net/pasaffe Author: Marc Deslauriers Author-email: marc.deslauriers@canonical.com License: GPL-3 Description: Pasaffe is an easy to use password manager for GNOME. Platform: UNKNOWN Requires: gi Requires: gi.repository.GLib Requires: gi.repository.GObject Requires: gi.repository.Gdk Requires: gi.repository.Gio Requires: gi.repository.Gtk Requires: gi.repository.Pango Requires: unidecode Provides: pasaffe Provides: pasaffe_lib ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1456351659.0 pasaffe-0.57/README0000644000175000017500000000236100000000000015407 0ustar00mdeslaurmdeslaur00000000000000Pasaffe ------- This is an easy-to-use password manager for GNOME. It stores information in a Password Safe 3.0 compatible database. Requirements: ------------- - python 3.x - pygobject - gtk+ 3.0 - gobject-introspection - pango1.0 - launchpad-integration (optional) - yelp - apg (for password generation) - unidecode Import scripts: --------------- Pasaffe contains some command-line utilities to import databases from other password applications: pasaffe-import-gpass: Imports GPass passwords directly from database. pasaffe-import-figaroxml: Imports Figaro's Password Manager 2 passwords from XML export file. pasaffe-import-keepassx: Imports KeePassX and KeePass2 passwords from XML export file. When using these utilities, please be aware that some fields may not have equivalents in Pasaffe and will not be imported. Always keep the original database file in case important data was in a field that wasn't imported. Project page: ------------- The project page is located here: https://launchpad.net/pasaffe Source code can be obtained here: https://code.launchpad.net/pasaffe Bugs about Pasaffe can be filed here: https://bugs.launchpad.net/pasaffe Contributions welcome! Please see the included TODO file for a list of things that are needed. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1573834473.0 pasaffe-0.57/README.development0000644000175000017500000000013700000000000017727 0ustar00mdeslaurmdeslaur00000000000000Please run the following before submitting fixes: Test suite: pytest-3 Code checker: flake8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1465743229.0 pasaffe-0.57/TODO0000644000175000017500000000072700000000000015223 0ustar00mdeslaurmdeslaur00000000000000List of stuff to do: - Improve folder support - Make search work with folder names - Fix loading expanded folders inside collapsed folders - Allow dragging more than one folder at a time - Improve icon - Add database merge functionality - Add better toolbar icons - Implement sanity check after saving db temp file - Implement translations for the help files - Add manpages for command-line tools - Add error dialog if attempting to add blank entry or folder name ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/bin/0000775000175000017500000000000000000000000015277 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457818259.0 pasaffe-0.57/bin/pasaffe0000755000175000017500000000337300000000000016636 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') # Add project root directory (enable symlink and trunk execution) PROJECT_ROOT_DIRECTORY = os.path.abspath( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) python_path = [] if os.path.abspath(__file__).startswith('/opt'): syspath = sys.path[:] # copy to avoid infinite loop in pending objects for path in syspath: opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe') python_path.insert(0, opt_path) sys.path.insert(0, opt_path) if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe')) and PROJECT_ROOT_DIRECTORY not in sys.path): python_path.insert(0, PROJECT_ROOT_DIRECTORY) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) if python_path: # for subprocesses os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) import pasaffe pasaffe.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457818936.0 pasaffe-0.57/bin/pasaffe-cli0000755000175000017500000004426700000000000017412 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2015 C de-Avillez # Based on work by Marc Deslauriers # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import getpass import sys import os import traceback from optparse import OptionParser import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') # Add project root directory (enable symlink and trunk execution) PROJECT_ROOT_DIRECTORY = os.path.abspath( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) python_path = [] if os.path.abspath(__file__).startswith('/opt'): syspath = sys.path[:] # copy to avoid infinite loop in pending objects for path in syspath: opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe') python_path.insert(0, opt_path) sys.path.insert(0, opt_path) if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe')) and PROJECT_ROOT_DIRECTORY not in sys.path): python_path.insert(0, PROJECT_ROOT_DIRECTORY) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) if python_path: # for subprocesses os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) from pasaffe_lib import readdb from pasaffe_lib.helpers import get_database_path from pasaffe_lib.helpers import gen_password # we work only on a subset of the PasswordSafe standard DICT = dict(UUID=1, GROUP=2, ENTRY=3, USERNAME=4, NOTES=5, PASSWORD=6, URL=13) def warn(text, quiet=False): if not quiet: print(text) def debug(text): if options.debug: print(text) def stack_dump(tpe, value, tracebk): if not options.debug: return print("exception: %s (%s)" % (tpe, value)) traceback.print_tb(tracebk) def get_options(): """ parse the run-time parameters. :return: (options, args) pair """ parser = OptionParser() parser.add_option("--debug", dest="debug", action="store_true", default=False, help=_("run with debug")) parser.add_option("-f", "--file", dest="filename", default=None, help=_("specify alternate GPass database file"), metavar="FILE") parser.add_option("--add", dest="action_add", action="store_true", default=False, help=_("add a new entry to the database")) parser.add_option("--repl", dest="action_replace", action="store_true", default=False, help=_("replace an entry in the database")) parser.add_option("--del", dest="action_delete", action="store_true", default=False, help=_("remove an entry from the database")) parser.add_option("--list", dest="action_list", action="store_true", default=False, help=_("print out a full entry in the database," " except the password")) parser.add_option("--listall", dest="action_listall", action="store_true", default=False, help=_("print out a full entry in the database," " except the password")) parser.add_option("--fuzzy", dest="fuzzy", action="store_true", default=False, help=_("look for --entry as a substring" " (default: False)")) parser.add_option("--listgroup", dest="listGroup", action="store_true", default=False, help=_("print out the group field of the given entry")) parser.add_option("--listuser", dest="listUser", action="store_true", default=False, help=_("print out the user field of the given entry")) parser.add_option("--listnotes", dest="listNotes", action="store_true", default=False, help=_("print out the Notes filed of the given entry")) parser.add_option("--listpswd", dest="listPswd", action="store_true", default=False, help=_("print out the password for the given entry")) parser.add_option("--listurl", dest="listURL", action="store_true", default=False, help=_("print out the URL field of the given entry")) parser.add_option("--createdb", dest="action_createDB", action="store_true", default=False, help=_("create a new Pasaffe database")) parser.add_option("-m", "--masterpassword", dest="master", default=None, help=_("specify Pasaffe database master password")) parser.add_option("--newmaster", dest="newmaster", default=None, help=_("specify new Pasaffe database master password")) parser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help=_("quiet messages")) parser.add_option("-e", "--entry", dest="entry", default=None, help=_("act on this database entry")) parser.add_option("--newentry", dest="newEntry", default=None, help=_("replacement entry string")) parser.add_option("-g", "--group", dest="group", default=None, help=_("group for this entry")) parser.add_option("-u", "--user", dest="user", default=None, help=_("userId for this entry")) parser.add_option("--pswd", dest="pswd", default=None, help=_("password/passphrase for this entry")) parser.add_option("--url", dest="url", default=None, help=_("URL for this entry")) parser.add_option("--notes", dest="notes", default=None, help=_("free-format notes for this entry")) parser.add_option("--genpswd", dest="gen_pswd", action="store_true", default=False, help="generates a password of ->-pswd_size> characters") parser.add_option("--pswdlen", dest="pswd_len", action="store", default=16, help="Generated password length." + " Defaults to 16 characters") return parser.parse_args() def check_options(args): """ Check the parameters passed on this run. Options are dealt with as follows: 1. --entry must always be provided (notice that --entry is overloaded: *if --list* is passed, then --entry is a substring of an entry title; *if --(add|repl|del), then --entry is the exact match of an entry title. 2. actions that change the DB are mutually exclusive with actions that list the DB. Either change or list, but not both, can be requested. 3. all fields in an entry are free fields. Anything goes. Your problem. 4. --list list all fields in the matching entries, *except* for the password. 5. --list_* will list the specific field requested (but not the other fields). So, if you want to: * list *all*: --list --list-paswd --entry= * list an entry, but no the password: --list --entry= * list URL and user: --list-url --list-user --entry= * and so on. 6. You cannot create a new DB *and* perform another action at the same time. Yeah, I am lazy. 7. So, mutually exclusive actions: action_*, createdb, gen_pswd :param args: command-line options :return: True if arg_options are not consistent, False otherwise """ # check for basic actions (add/replace/delete) fail = False actions = 0 if args.action_add: # opinions vary. Should we accept *only the entry name? I think at # LEAST we have to have --user. actions += 1 if args.user is None: warn(_("when adding an entry," " at least the userId must be provided")) fail = True if args.action_replace: actions += 1 if (args.newEntry is None and args.group is None and args.user is None and args.pswd is None and args.url is None and args.notes is None): warn(_("--repl MUST be used with at least one of --newentry," " --group, --user, --pswd, --url, or --notes")) fail = True if args.action_delete: # if deleting an entry in the DB, we do not care for anything else # but --entry (checked for below) actions += 1 if args.action_createDB: actions += 1 if args.action_list: args.listUser = True args.listNotes = True args.listGroup = True if actions > 1: warn(_("ERROR: only one of --createdb, --list, --add, --repl," " or --del can be specified")) fail = True # check for list actions if args.listPswd \ or args.listUser \ or args.listURL: if actions != 0: warn(_("cannot use --list* with any of --add, --del, or --repl")) fail = True # no matter what, --entry MUST be specified if args.entry is None \ and args.action_createDB is False \ and not args.gen_pswd: warn(_("one of --entry, --createdb, or --genpswd MUST be provided")) fail = True # check if we have a file name if args.filename is None: args.filename = get_database_path() debug("DB filename resolved to %s" % os.path.realpath(args.filename)) return fail def find_entry(fuzzy=False): """ Find an entry in the database. :param fuzzy: if True, search for a substring of the actual entry, otherwise search for an exact match :return: list of entries (may be empty, one single entry, or many) """ match_set = [] # deal with a potential "--list-all" if options.entry == "*": return pswd_file.records.values() for record in pswd_file.records.values(): if fuzzy: if options.entry in record[3]: match_set.append(record) else: if options.entry == record[3]: match_set.append(record) return match_set def action_createDB(): if os.path.exists(options.filename): warn(_("ERROR: --createdb requested, but there is already a" " file with the same name")) sys.exit(1) debug("creating new db under %s" % os.path.realpath('.')) if options.master is None: options.master = getpass.getpass(_("Password> ")) try: pswd_file.new_db(options.master) saveDB() except: warn(_("error creating new database")) (err_type, err_value, err_tracebk) = sys.exc_info() stack_dump(err_type, err_value, err_tracebk) return False return True def readDB(): try: pswd_file.readfile(options.filename, options.master) except ValueError: warn(_("Could not read the database, bad password?")) exit(1) def saveDB(backIt=False): pswd_file.writefile(options.filename, backup=backIt) def action_replace(): if options.entry is None: warn(_("need --entry to replace fields in an entry")) exit(1) records = find_entry() if len(records) == 0: warn(_("did not find any matching entry")) exit(1) if len(records) != 1: warn(_("found more than one entry to change, cannot change")) exit(1) repl = records[0] uuid = repl[DICT['UUID']] if options.newEntry is not None: repl[DICT['ENTRY']] = options.newEntry if options.user is not None: repl[DICT['USERNAME']] = options.user if options.group is not None: repl[DICT['GROUP']] = options.group if options.pswd is not None: repl[DICT['PASSWORD']] = options.pswd if options.url is not None: repl[DICT['URL']] = options.url if options.notes is not None: repl[DICT['NOTES']] = options.notes pswd_file.records[uuid] = repl saveDB(backIt=True) def action_add(): """ Add a given entry to the database. :return: nothing """ # if we are adding an entry, it cannot already exist if options.entry is None: warn(_("need --entry to add an entry to the database")) exit(1) if find_entry() != []: warn(_("provided --entry already exists in the database")) exit(1) # populate fields new_entry = pswd_file.new_entry() pswd_file.records[new_entry][DICT['ENTRY']] = options.entry if options.user is not None: pswd_file.records[new_entry][DICT['USERNAME']] = options.user if options.group is not None: pswd_file.records[new_entry][DICT['GROUP']] = options.group if options.pswd is not None: pswd_file.records[new_entry][DICT['PASSWORD']] = options.pswd if options.url is not None: pswd_file.records[new_entry][DICT['URL']] = options.url if options.notes is not None: pswd_file.records[new_entry][DICT['NOTES']] = options.notes saveDB(backIt=True) def action_list(): """ List all entries in the matches list, respecting what was asked to be printed out. if options.action_list was requested, then all fields will have labels; otherwise, only the requested values will be printed. :return: nothing """ matches = find_entry(fuzzy=options.fuzzy) if matches is None: warn(_("did not find hits for %s in the database" % options.entry)) return for record in matches: # ['Group', 'Entry', 'Username', 'Password', 'URL', 'Notes']: if options.listGroup and DICT["GROUP"] in record: out_line = "" if options.action_list: out_line = "Group: " print("%s%s" % (out_line, record[DICT["GROUP"]])) if options.action_list: print("Entry: %s" % record[DICT["ENTRY"]]) if options.listUser and DICT["USERNAME"] in record: out_line = "" if options.action_list: out_line = "Username: " print("%s%s" % (out_line, record[DICT["USERNAME"]])) if options.listPswd and DICT["PASSWORD"] in record: out_line = "" if options.action_list: out_line = "Password: " print("%s%s" % (out_line, record[DICT["PASSWORD"]])) if options.listURL and DICT["URL"] in record: out_line = "" if options.action_list: out_line = "URL: " print("%s%s" % (out_line, record[DICT["URL"]])) if options.listNotes and DICT["NOTES"] in record: out_line = "" if options.action_list: out_line = "Notes: " print("%s%s" % (out_line, record[DICT["NOTES"]])) if options.action_list: print("") def action_delete(): """ Delete an entry from the database. options.entry must be an exact match for the entry. :return: nothing """ match = find_entry() if len(match) == 0: warn(_("provided --entry was not found in the database")) exit(1) if len(match) != 1: warn(_("provided --entry occurs multiple times, cannot delete")) exit(1) # OK, delete the beast pswd_file.delete_entry(match[DICT['UUID']]) saveDB(backIt=True) def gen_pswd(length): """ Generates a random password :param length: length of password :return: generated password """ pswd = gen_password(1, length)[0] return pswd.decode('utf-8').strip() # # mainline # (options, args) = get_options() if check_options(options): warn(_("terminating run")) exit(1) # try the easy one first if options.gen_pswd: new_pswd = gen_pswd(options.pswd_len) if new_pswd is None: exit(1) else: print("%s\n" % new_pswd) exit(0) if options.master is None: options.master = getpass.getpass(_("Password> ")) # OK, we can now initialise the DB structure pswd_file = readdb.PassSafeFile() if options.action_createDB: if action_createDB(): sys.exit(0) else: sys.exit(1) if not os.path.exists(options.filename): warn(_("\nERROR: Could not locate database file!")) sys.exit(1) readDB() if options.newmaster is not None: new_master = pswd_file.new_keys(options.newmaster) saveDB(backIt=True) exit(0) if options.action_list or options.listUser or options.listURL or \ options.listPswd: action_list() elif options.action_add: action_add() elif options.action_delete: action_delete() elif options.action_replace: action_replace() sys.exit(0) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457819973.0 pasaffe-0.57/bin/pasaffe-dump-db0000755000175000017500000000770100000000000020163 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011 Jamie Strandboge # Based on work by Marc Deslauriers # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import getpass import sys import os import struct import time from optparse import OptionParser import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') # Add project root directory (enable symlink and trunk execution) PROJECT_ROOT_DIRECTORY = os.path.abspath( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) python_path = [] if os.path.abspath(__file__).startswith('/opt'): syspath = sys.path[:] # copy to avoid infinite loop in pending objects for path in syspath: opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe') python_path.insert(0, opt_path) sys.path.insert(0, opt_path) if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe')) and PROJECT_ROOT_DIRECTORY not in sys.path): python_path.insert(0, PROJECT_ROOT_DIRECTORY) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) if python_path: # for subprocesses os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) from pasaffe_lib import readdb from pasaffe_lib.helpers import get_database_path parser = OptionParser() parser.add_option("-f", "--file", dest="filename", help="specify alternate GPass database file", metavar="FILE") parser.add_option("-m", "--masterpassword", dest="master", default=None, help="specify Pasaffe database master password") parser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help="quiet messages") parser.add_option("-d", "--dump", dest="dump", action="store_true", default=False, help="print raw dump") (options, args) = parser.parse_args() if options.filename is None: db_filename = get_database_path() else: db_filename = options.filename if not os.path.exists(db_filename): print("\n\nERROR: Could not locate database file!") sys.exit(1) if not options.quiet: print("WARNING: this will display all password entries.") if options.dump: fixup = False else: fixup = True count = 0 max_tries = 3 while count < max_tries: count += 1 if options.master is not None: master = options.master else: master = getpass.getpass("Password> ") try: passfile = readdb.PassSafeFile(db_filename, master, fixup=fixup) break except ValueError: print("Sorry, try again.") if count >= max_tries: print(("%d incorrect password attempts" % (count))) sys.exit(1) record_dict = {'Entry': 3, 'Username': 4, 'Notes': 5, 'Password': 6, 'URL': 13 } if options.dump: print("Printing raw dump of database:\n") print(passfile.records) sys.exit(0) for record in sorted(list(passfile.records.values()), key=lambda entry: entry[3].lower()): # specify order of labels and values for label in ['Entry', 'Username', 'Password', 'URL', 'Notes']: record_type = record_dict[label] if record_type in record and record[record_type] != "": print(("%s: %s" % (label, record[record_type]))) print("") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457818970.0 pasaffe-0.57/bin/pasaffe-import-entry0000755000175000017500000000770200000000000021305 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os import struct import time from optparse import OptionParser import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') # Add project root directory (enable symlink and trunk execution) PROJECT_ROOT_DIRECTORY = os.path.abspath( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) python_path = [] if os.path.abspath(__file__).startswith('/opt'): syspath = sys.path[:] # copy to avoid infinite loop in pending objects for path in syspath: opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe') python_path.insert(0, opt_path) sys.path.insert(0, opt_path) if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe')) and PROJECT_ROOT_DIRECTORY not in sys.path): python_path.insert(0, PROJECT_ROOT_DIRECTORY) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) if python_path: # for subprocesses os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) from pasaffe_lib import readdb from pasaffe_lib import set_up_logging, get_version from pasaffe_lib.helpers import get_database_path parser = OptionParser() parser.add_option("-v", "--verbose", action="count", dest="verbose", help="Show debug messages (-vv debugs pasaffe_lib also)") parser.add_option("-f", "--file", dest="filename", help="specify alternate Pasaffe database file", metavar="FILE") parser.add_option("-m", "--master", dest="master", default='', help="specify database master password") parser.add_option("-e", "--entry", dest="entry", default='', help="name for new entry") parser.add_option("-l", "--url", dest="url", default='', help="specify URL") parser.add_option("-u", "--username", dest="username", default='', help="specify entry username") parser.add_option("-p", "--password", dest="password", default='', help="specify entry password") parser.add_option("-n", "--notes", dest="notes", default='', help="specify entry notes") parser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help="quiet messages") (options, args) = parser.parse_args() set_up_logging(options) if options.filename is not None: db_filename = options.filename else: db_filename = get_database_path() if not options.quiet: print("Attempting to import new entry...", end=' ') if not os.path.exists(db_filename): print("\n\nERROR: Could not locate database file!") sys.exit(1) if options.entry == '': print("\n\nERROR: New entry must at least have a name!") sys.exit(1) if options.master == '': print("\n\nERROR: Must specify database master password!") sys.exit(1) passfile = readdb.PassSafeFile(db_filename, options.master) entry = passfile.new_entry() passfile.records[entry][3] = options.entry passfile.records[entry][4] = options.username passfile.records[entry][5] = options.notes passfile.records[entry][6] = options.password passfile.records[entry][13] = options.url passfile.writefile(db_filename, backup=True) if not options.quiet: print("Success!") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457819002.0 pasaffe-0.57/bin/pasaffe-import-figaroxml0000755000175000017500000001415700000000000022136 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011 Marc Deslauriers # Copyright (C) 2011 Francesco Marella # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os import getpass import shutil from optparse import OptionParser import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') # Add project root directory (enable symlink and trunk execution) PROJECT_ROOT_DIRECTORY = os.path.abspath( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) python_path = [] if os.path.abspath(__file__).startswith('/opt'): syspath = sys.path[:] # copy to avoid infinite loop in pending objects for path in syspath: opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe') python_path.insert(0, opt_path) sys.path.insert(0, opt_path) if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe')) and PROJECT_ROOT_DIRECTORY not in sys.path): python_path.insert(0, PROJECT_ROOT_DIRECTORY) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) if python_path: # for subprocess os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) from pasaffe_lib import figaroxml from pasaffe_lib import readdb from pasaffe_lib import set_up_logging, get_version from pasaffe_lib.helpers import confirm, get_database_path parser = OptionParser() parser.add_option("-v", "--verbose", action="count", dest="verbose", help="Show debug messages (-vv debugs pasaffe_lib also)") parser.add_option("-f", "--file", dest="filename", help="specify FPM2 XML file", metavar="FILE") parser.add_option("-d", "--database", dest="database", default=None, help="specify alternate Pasaffe database file") parser.add_option("-o", "--overwrite", dest="overwrite", action="store_true", default=False, help="overwrite existing Pasaffe password store") parser.add_option("-y", "--yes", dest="yes", action="store_true", default=False, help="don't ask for confirmation (may result in data loss!)") parser.add_option("-m", "--masterpassword", dest="master", default=None, help="specify Pasaffe database master password") parser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help="quiet messages") (options, args) = parser.parse_args() set_up_logging(options) if options.filename is None: print("You must specify the name of the FPM2 XML file!\n") parser.print_help() sys.exit(1) else: filename = options.filename if not options.quiet: print("Attempting to import FPM2 passwords...") print("Database filename is %s" % filename) print() if not os.path.exists(filename): print("Could not locate database file!") sys.exit(1) if options.database is None: db_filename = get_database_path() else: db_filename = options.database fpmxml = figaroxml.FigaroXML(filename) items = len(fpmxml.records) if items == 0: print("Database was empty!") sys.exit(1) else: if not options.quiet: print("Located %s passwords in the database!" % items) print() if not options.quiet and len(fpmxml.skipped) > 0: print("WARNING: The following fields will be ignored by this script:") print(" ".join(fpmxml.skipped)) print("Please keep a copy of your original FPM2 database, as the") print("content of those fields will not be imported into Pasaffe.") print() if options.yes is True: if options.overwrite is True and os.path.exists(db_filename): shutil.copy(db_filename, db_filename + ".bak") os.unlink(db_filename) else: if not os.path.exists(db_filename): print("WARNING: Could not locate a Pasaffe database.") response = confirm(prompt='Create a new database?', resp=False) elif options.overwrite is True: print("If you continue, your current Pasaffe database will be" " DELETED.") response = confirm(prompt='Overwrite database?', resp=False) if response is True: shutil.copy(db_filename, db_filename + ".bak") os.unlink(db_filename) else: print("If you continue, passwords will be imported into Pasaffe.") response = confirm(prompt='Import to database?', resp=False) if response is False: print("Aborting.") sys.exit(1) # Get password for Pasaffe database if os.path.exists(db_filename): if options.master is not None: password = options.master else: print("You must now enter the Pasaffe database password.") password = getpass.getpass() passsafe = readdb.PassSafeFile(db_filename, password) for entry in fpmxml.records: passsafe.records[entry] = fpmxml.records[entry] passsafe.writefile(db_filename, backup=True) else: if options.master is not None: password = options.master else: print("You now must enter a master password for the new" " Pasaffe database") while(1): password = getpass.getpass("New password: ") password_conf = getpass.getpass("Confirm password: ") if password != password_conf: print("ERROR: passwords don't match, try again.\n\n") else: break passsafe = readdb.PassSafeFile() passsafe.new_db(password) passsafe.records = fpmxml.records passsafe.writefile(db_filename) if not options.quiet: print("Success!") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457819011.0 pasaffe-0.57/bin/pasaffe-import-gpass0000755000175000017500000001377000000000000021263 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os import getpass import shutil from optparse import OptionParser import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') # Add project root directory (enable symlink and trunk execution) PROJECT_ROOT_DIRECTORY = os.path.abspath( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) python_path = [] if os.path.abspath(__file__).startswith('/opt'): syspath = sys.path[:] # copy to avoid infinite loop in pending objects for path in syspath: opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe') python_path.insert(0, opt_path) sys.path.insert(0, opt_path) if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe')) and PROJECT_ROOT_DIRECTORY not in sys.path): python_path.insert(0, PROJECT_ROOT_DIRECTORY) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) if python_path: # for subprocesses os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) from pasaffe_lib import gpassfile from pasaffe_lib import readdb from pasaffe_lib import set_up_logging, get_version from pasaffe_lib.helpers import confirm, get_database_path parser = OptionParser() parser.add_option("-v", "--verbose", action="count", dest="verbose", help="Show debug messages (-vv debugs pasaffe_lib also)") parser.add_option("-f", "--file", dest="filename", help="specify alternate GPass database file", metavar="FILE") parser.add_option("-d", "--database", dest="database", default=None, help="specify alternate Pasaffe database file") parser.add_option("-o", "--overwrite", dest="overwrite", action="store_true", default=False, help="overwrite existing Pasaffe password store") parser.add_option("-y", "--yes", dest="yes", action="store_true", default=False, help="don't ask for confirmation (may result in data loss!)") parser.add_option("-p", "--password", dest="password", default=None, help="specify GPass database password") parser.add_option("-m", "--masterpassword", dest="master", default=None, help="specify Pasaffe database master password") parser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help="quiet messages") (options, args) = parser.parse_args() set_up_logging(options) if options.filename is None: filename = os.path.join(os.environ['HOME'], '.gpass/passwords.gps') else: filename = options.filename if not options.quiet: print("Attempting to import GPass passwords...") print("Database filename is %s" % filename) print() if not os.path.exists(filename): print("Could not locate database file!") sys.exit(1) if options.database is None: db_filename = get_database_path() else: db_filename = options.database if options.password is None: password = getpass.getpass() else: password = options.password gpass = gpassfile.GPassFile(filename, password) items = len(gpass.records) if items == 0: print("Database was empty!") sys.exit(1) else: if not options.quiet: print("Located %s passwords in the database!" % items) if options.yes is True: if options.overwrite is True and os.path.exists(db_filename): shutil.copy(db_filename, db_filename + ".bak") os.unlink(db_filename) else: if not os.path.exists(db_filename): print("WARNING: Could not locate a Pasaffe database.") response = confirm(prompt='Create a new database?', resp=False) elif options.overwrite is True: print("If you continue, your current Pasaffe database will" " be DELETED.") response = confirm(prompt='Overwrite database?', resp=False) if response is True: shutil.copy(db_filename, db_filename + ".bak") os.unlink(db_filename) else: print("If you continue, passwords will be imported into Pasaffe.") response = confirm(prompt='Import to database?', resp=False) if response is False: print("Aborting.") sys.exit(1) # Get password for Pasaffe database if os.path.exists(db_filename): if options.master is not None: password = options.master else: print("You must now enter the Pasaffe database password.") password = getpass.getpass() passsafe = readdb.PassSafeFile(db_filename, password) for entry in gpass.records: passsafe.records[entry] = gpass.records[entry] passsafe.writefile(db_filename, backup=True) else: if options.master is not None: password = options.master else: print("You now must enter a master password for the new" " Pasaffe database") while(1): password = getpass.getpass("New password: ") password_conf = getpass.getpass("Confirm password: ") if password != password_conf: print("ERROR: passwords don't match, try again.\n\n") else: break passsafe = readdb.PassSafeFile() passsafe.new_db(password) passsafe.records = gpass.records passsafe.empty_folders = gpass.empty_folders passsafe.writefile(db_filename) if not options.quiet: print("Success!") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457819020.0 pasaffe-0.57/bin/pasaffe-import-keepassx0000755000175000017500000001416000000000000021763 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os import getpass import shutil from optparse import OptionParser import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') # Add project root directory (enable symlink and trunk execution) PROJECT_ROOT_DIRECTORY = os.path.abspath( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) python_path = [] if os.path.abspath(__file__).startswith('/opt'): syspath = sys.path[:] # copy to avoid infinite loop in pending objects for path in syspath: opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe') python_path.insert(0, opt_path) sys.path.insert(0, opt_path) if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe')) and PROJECT_ROOT_DIRECTORY not in sys.path): python_path.insert(0, PROJECT_ROOT_DIRECTORY) sys.path.insert(0, PROJECT_ROOT_DIRECTORY) if python_path: # for subprocess os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) from pasaffe_lib import keepassx from pasaffe_lib import readdb from pasaffe_lib import set_up_logging, get_version from pasaffe_lib.helpers import confirm, get_database_path parser = OptionParser() parser.add_option("-v", "--verbose", action="count", dest="verbose", help="Show debug messages (-vv debugs pasaffe_lib also)") parser.add_option("-f", "--file", dest="filename", help="specify KeePassX or KeePass2 XML file", metavar="FILE") parser.add_option("-d", "--database", dest="database", default=None, help="specify alternate Pasaffe database file") parser.add_option("-o", "--overwrite", dest="overwrite", action="store_true", default=False, help="overwrite existing Pasaffe password store") parser.add_option("-y", "--yes", dest="yes", action="store_true", default=False, help="don't ask for confirmation (may result in data loss!)") parser.add_option("-m", "--masterpassword", dest="master", default=None, help="specify Pasaffe database master password") parser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help="quiet messages") (options, args) = parser.parse_args() set_up_logging(options) if options.filename is None: print("You must specify the name of the KeePassX/KeePass2 XML file!\n") parser.print_help() sys.exit(1) else: filename = options.filename if not options.quiet: print("Attempting to import KeePassX passwords...") print("Database filename is %s" % filename) print() if not os.path.exists(filename): print("Could not locate database file!") sys.exit(1) if options.database is None: db_filename = get_database_path() else: db_filename = options.database keepassx = keepassx.KeePassX(filename) items = len(keepassx.records) if items == 0: print("Database was empty!") sys.exit(1) else: if not options.quiet: print("Located %s passwords in the database!" % items) print() if not options.quiet and len(keepassx.skipped) > 0: print("WARNING: The following fields will be ignored by this script:") print(" ".join(keepassx.skipped)) print("Please keep a copy of your original KeePassX/KeePass2 database, as") print("the content of those fields will not be imported into Pasaffe.") print() if options.yes is True: if options.overwrite is True and os.path.exists(db_filename): shutil.copy(db_filename, db_filename + ".bak") os.unlink(db_filename) else: if not os.path.exists(db_filename): print("WARNING: Could not locate a Pasaffe database.") response = confirm(prompt='Create a new database?', resp=False) elif options.overwrite is True: print("If you continue, your current Pasaffe database" " will be DELETED.") response = confirm(prompt='Overwrite database?', resp=False) if response is True: shutil.copy(db_filename, db_filename + ".bak") os.unlink(db_filename) else: print("If you continue, passwords will be imported into Pasaffe.") response = confirm(prompt='Import to database?', resp=False) if response is False: print("Aborting.") sys.exit(1) # Get password for Pasaffe database if os.path.exists(db_filename): if options.master is not None: password = options.master else: print("You must now enter the Pasaffe database password.") password = getpass.getpass() passsafe = readdb.PassSafeFile(db_filename, password) for entry in keepassx.records: passsafe.records[entry] = keepassx.records[entry] passsafe.writefile(db_filename, backup=True) else: if options.master is not None: password = options.master else: print("You now must enter a master password for the new" " Pasaffe database") while(1): password = getpass.getpass("New password: ") password_conf = getpass.getpass("Confirm password: ") if password != password_conf: print("ERROR: passwords don't match, try again.\n\n") else: break passsafe = readdb.PassSafeFile() passsafe.new_db(password) passsafe.records = keepassx.records passsafe.writefile(db_filename) if not options.quiet: print("Success!") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/data/0000775000175000017500000000000000000000000015440 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/data/glib-2.0/0000775000175000017500000000000000000000000016652 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/data/glib-2.0/schemas/0000775000175000017500000000000000000000000020275 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1569327657.0 pasaffe-0.57/data/glib-2.0/schemas/net.launchpad.pasaffe.gschema.xml0000644000175000017500000001050100000000000026550 0ustar00mdeslaurmdeslaur00000000000000 "" Pasaffe database location Full path to the location where Pasaffe stores the password database. "collapsed" Pasaffe folder expansion preference How Pasaffe expands the folder tree when starting. "edits" Pasaffe double-click behaviour How Pasaffe handles an entry being double-clicked. false Display usernames next to entry names When this is true, Pasaffe will display usernames next to entry names. 5 Timeout before Pasaffe is locked Number of minutes of inactivity before Pasaffe locks itself. true Lock window after inactivity When this is true, the Pasaffe window will automatically lock after inactivity. true Only passwords are considered to be secret When this is true, only the password field is hidden when secrets aren't visible. false Secrets should be visible by default When this is true, secrets are visible in the details pane without user intervention. false Automatically save changes When this is true, Pasaffe will automatically save the database. 12 Length of generated passwords Number of characters to generate for random passwords. 20 Timeout before clipboard is cleared Number of seconds before Pasaffe clears the clipboard after using it. 600 Height of main window Sets the current vertical size of the main window. 600 Width of main window Sets the current horizontal size of the main window. 300 Width of main window split Sets the current horizontal split of the main window. 350 Height of entry window Sets the current vertical size of the entry window. 475 Width of entry window Sets the current horizontal size of the entry window. 150 Height of folder window Sets the current vertical size of the folder window. 350 Width of folder window Sets the current horizontal size of the folder window. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/data/icons/0000775000175000017500000000000000000000000016553 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/data/icons/scalable/0000775000175000017500000000000000000000000020321 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/data/icons/scalable/apps/0000775000175000017500000000000000000000000021264 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/icons/scalable/apps/pasaffe.svg0000644000175000017500000002356300000000000023421 0ustar00mdeslaurmdeslaur00000000000000 image/svg+xml ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/data/ui/0000775000175000017500000000000000000000000016055 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1513439904.0 pasaffe-0.57/data/ui/AboutPasaffeDialog.ui0000644000175000017500000000520000000000000022067 0ustar00mdeslaurmdeslaur00000000000000 False 5 pasaffe normal Pasaffe Copyright © 2011-2013 Marc Deslauriers <marc.deslauriers@canonical.com> http://www.launchpad.net/pasaffe http://www.launchpad.net/pasaffe This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Marc Deslauriers <marc.deslauriers@canonical.com> Jamie Strandboge <jamie@canonical.com> Francesco Marella <fra.marella@gmx.com> C de-Avillez <hggdh2@ubuntu.com> translator-credits pasaffe True False 2 True False end False True end 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1513442510.0 pasaffe-0.57/data/ui/EditDetailsDialog.ui0000644000175000017500000004053000000000000021727 0ustar00mdeslaurmdeslaur00000000000000 True False system-run-symbolic False 5 Pasaffe 400 pasaffe normal True False vertical 5 True False end _Cancel True True True True False False 0 _OK True True True True False False 1 False True end 2 True False <big><b>Edit entry</b></big> True True True 0 True False 5 5 True True The name for this entry True False False 1 0 True False The folder for this entry True liststore1 True 0 True 1 1 True True The entry's username True False False 1 3 True False 20 20 Title: 0 0 True False 20 20 Folder: 0 1 True False 20 20 Username: 0 3 True False 20 20 Password: 0 4 True False 20 20 Notes: 0 5 True False 20 20 URL: 0 2 True True The website for this entry True False False 1 2 114 True True True 114 True True notes_buffer 1 5 True False True True True False False True True 0 True True True Pick a random password image1 False False 1 1 4 True True 5 1 btn_cancel btn_ok True False True False password1 True True False password2 True True False password3 True True False password4 True True False password5 True True False password6 True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1513443000.0 pasaffe-0.57/data/ui/EditFolderDialog.ui0000644000175000017500000001606500000000000021563 0ustar00mdeslaurmdeslaur00000000000000 False 5 pasaffe normal True False vertical 2 True False end _Cancel True True True True False False 0 _OK True True True True True True False False 1 False True end 0 True False <big><b>Edit folder</b></big> True False True 1 True False 5 5 True False 20 20 Parent folder: 0 0 True False 20 20 Folder name: 0 1 True False The parent folder for this new folder liststore1 True 0 False False 1 0 True True True True True True True 1 1 True True 5 2 btn_cancel btn_ok ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1513442869.0 pasaffe-0.57/data/ui/NewDatabaseDialog.ui0000644000175000017500000002462500000000000021721 0ustar00mdeslaurmdeslaur00000000000000 False 5 Pasaffe pasaffe normal True False vertical 2 True False end _Cancel True True True True False False 0 _OK True True True True True True False False 1 False True end 0 True False True False dialog-password-symbolic 6 False True 0 True False vertical 4 True False <big><b>Welcome to Pasaffe!</b></big> True center True True 5 0 True False No existing database was found. A new one will be created. Please enter a master password to protect it. center True True True 5 1 False Passwords don't match! Please try again. True True 10 2 True False True False 20 20 Enter password: 0 0 True False 20 20 Confirm password: 0 1 True True False True False False 1 0 True True False True False False 1 1 True True 5 3 True True 1 True True 1 btn_cancel btn_ok ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1513442802.0 pasaffe-0.57/data/ui/NewPasswordDialog.ui0000644000175000017500000002744700000000000022024 0ustar00mdeslaurmdeslaur00000000000000 False 5 Pasaffe pasaffe normal True False vertical 2 True False end _Cancel True True True True False False 0 _OK True True True True True True False False 1 False True end 0 True False True False dialog-password-symbolic 6 True True 0 True False vertical 4 True False <big><b>Change master password</b></big> True True True 5 0 True False Enter your old master password, and a new master password. You must confirm the new password. center True True 5 1 False error message True True 5 2 True False True True False True False False 1 0 True True False True False False 1 1 True True False True False False 1 2 True False 20 20 Old password: 0 0 True False 20 20 New password: 0 1 True False 20 20 Confirm new password: 0 2 True True 5 3 True True 1 True True 1 btn_cancel btn_ok ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1569316341.0 pasaffe-0.57/data/ui/PasaffeWindow.ui0000644000175000017500000012647500000000000021166 0ustar00mdeslaurmdeslaur00000000000000 False True False window-close-symbolic True False go-up-symbolic True False go-down-symbolic False Pasaffe 600 600 pasaffe True False vertical 5 True False True False _File True True False _Save True False True False True False Lock True False False True False Change Master Password True False False True False _Close True False True False True False _Edit True True False True False Add Entry True True False Add Folder True Clone True False False _Delete True False True False True False True False Find True True False True False Copy URL True False Copy Username True False Copy Password True False _Preferences True False True False True False _View True True False True False Display Secrets True False Open URL True False False True False Database Information True False False True False _Help True True False Contents True False False _About True False True False False True 0 True False True False Save changes Save True document-save-symbolic False True True False False False True False Add a new entry Add True document-new-symbolic False True True False Add a new folder Add Folder True folder-new-symbolic False True True False Edit the selected entry Edit True document-properties-symbolic False True True False Delete the selected entry Remove True edit-delete-symbolic False True True False False False True False Open the URL Open URL True go-home-symbolic False True True False Copy the username Copy Username True system-users-symbolic False True True False Copy the password Copy Password True star-new-symbolic False True True False False False True False Find entries Find True edit-find-symbolic False True True False False False True False Display secrets Display Secrets True view-more-horizontal-symbolic False True False True 1 True True 300 True True True True liststore1 False 1 True autosize icon 0 name 1 True False 300 True False vertical False True True True Close find image17 False False 0 True True edit-find-symbolic False False True True 1 True True True Previous result image18 False False 2 True True True Next result image19 False False 3 False False 0 True True True True False word 10 10 True True 1 True True True True 2 False True False vertical True False True True 0 True False <big><b>The password database is currently locked.</b></big> True False False 10 1 True False spread _Quit True True True True False False 0 Unlock True True True True True False False 1 False False 2 True False True True 3 True False True False Copy Username True False Copy Password True False Copy URL True False True False Add Entry True True False Add Folder True _Edit True False True False Clone True False False _Delete True False True False ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1513442902.0 pasaffe-0.57/data/ui/PasswordEntryDialog.ui0000644000175000017500000001557100000000000022367 0ustar00mdeslaurmdeslaur00000000000000 False 5 Pasaffe False pasaffe normal True False vertical 2 True False end _Cancel True True True True False False 0 _OK True True True True True True False False 1 False True end 0 True False True False dialog-password-symbolic 6 False True 0 True False vertical True False <big><b>Please enter your master password:</b></big> True True True 5 0 False Invalid password! Please try again. True True 5 1 True True False True False False True False 5 2 True True 5 1 True True 1 btn_cancel btn_ok ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1569327516.0 pasaffe-0.57/data/ui/PreferencesPasaffeDialog.ui0000644000175000017500000005172700000000000023275 0ustar00mdeslaurmdeslaur00000000000000 100 1 10 100 1 10 start expanded expanded start collapsed collapsed are remembered from last save saved edits entry edits copies password copies False 5 Pasaffe Preferences pasaffe normal True False vertical 5 True False end _Help False True True True True False False 0 True _Close False True True True True False False 1 False True 10 end 5 Automatically save changes True True False True False True 3 0 True False 0 none True False vertical Display secrets by default True True False True False True 0 Only passwords are secret True True False True False True 1 True False <b>Secrets</b> True False True 3 1 True False 0 none True False Lock database after True True False True False False 0 True True 2 False False adjustment1 True False False 5 1 True False minutes False False 2 True False <b>Lock</b> True False True 3 2 True False 0 none True False True False Passwords are False False 0 True True 2 False False adjustment2 True False False 5 1 True False characters long False False 2 True False <b>Password Generator</b> True False True 3 3 True False 0 none True False vertical True False True False Folders False False 0 True True liststore1 1 0 False False 5 1 False True 0 True False True False Double-click False False 0 True False liststore2 1 0 False False 5 1 False True 1 Display usernames in tree True True False True False True 2 True False <b>Folder Preferences</b> True False True 3 4 btn_help btn_close ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1513441625.0 pasaffe-0.57/data/ui/SaveChangesDialog.ui0000644000175000017500000001471500000000000021731 0ustar00mdeslaurmdeslaur00000000000000 False 5 Pasaffe pasaffe normal True False vertical 10 True False end Close without Saving True True False False False 0 _Cancel True True False True False False 1 _Save True True True True True True False False 2 False True end 1 True False True False dialog-warning-symbolic 6 True True 0 True False vertical 10 True False <big><b>Save changes to your password database before closing?</b></big> True True True 0 True False Your changes will be lost if you don't save them. True True 1 True True 5 1 True True 0 btn_close btn_cancel btn_save ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/about_pasaffe_dialog.xml0000644000175000017500000000061700000000000022717 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/edit_details_dialog.xml0000644000175000017500000000060200000000000022544 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/edit_folder_dialog.xml0000644000175000017500000000057600000000000022404 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/new_database_dialog.xml0000644000175000017500000000060200000000000022527 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/new_password_dialog.xml0000644000175000017500000000060200000000000022625 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/pasaffe_window.xml0000644000175000017500000000055500000000000021576 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/password_entry_dialog.xml0000644000175000017500000000061200000000000023176 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/preferences_pasaffe_dialog.xml0000644000175000017500000000063300000000000024104 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/data/ui/save_changes_dialog.xml0000644000175000017500000000060200000000000022540 0ustar00mdeslaurmdeslaur00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1390520544.0 pasaffe-0.57/do-release0000755000175000017500000000257000000000000016477 0ustar00mdeslaurmdeslaur00000000000000#!/bin/sh -e # This script does all the steps necessary for doing a new upstream release. It # should solely be used by upstream developers, distributors do not need to # worry about it. [ -z "`bzr status`" ] || { echo "Uncommitted changes, aborting" >&2 exit 1 } # remove backup files find . -type f -name "*.~?~" -exec rm {} \; # remove compiled gschema file rm -f data/glib-2.0/schemas/gschemas.compiled # remove Python 3 cache files rm -rf pasaffe/__pycache__ rm -rf pasaffe_lib/__pycache__ version=$(grep '(UNRELEASED)' NEWS | cut -f1 -d' ') [ -n "$version" ] || { echo "no UNRELEASED in NEWS" >&2 exit 1 } # check manifest ./setup.py build ./setup.py sdist -o ./setup.py clean -a missing=$(for f in `find -type f ! -path '*.bzr*' ! -name '*.pyc' ! -name 'MANIFEST*' ! -name do-release ! -name TODO`; do grep -qFx "${f#./}" < MANIFEST || echo $f; done) if [ -n "$missing" ]; then echo "MANIFEST is missing the following files:" echo "$missing" exit 1; fi set -x sed -i -r "s/(^[[:space:]]*version[[:space:]]*=[[:space:]]*').*\$/\\1${version}',/" setup.py sed -i "s/(UNRELEASED)/$(date '+(%Y-%m-%d)')/" NEWS #dch -v ${version}-1 "New version" bzr commit -m "release $version" bzr tag "$version" bzr log -v --gnu-changelog > ChangeLog ./setup.py sdist -d .. ./setup.py clean -a rm ChangeLog MANIFEST gpg --armor --sign --detach-sig ../pasaffe-$version.tar.gz ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/docs/0000775000175000017500000000000000000000000015457 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/docs/formatV3.txt0000644000175000017500000006352600000000000017733 0ustar00mdeslaurmdeslaur00000000000000PasswordSafe database format description version 3.30 ----------------------------------------------------- Copyright (c) 2003-2013 Rony Shapiro . All rights reserved. Use of the code is allowed under the Artistic License terms, as specified in the LICENSE file distributed with this code, or available from http://www.opensource.org/licenses/artistic-license-2.0.php 1. Introduction: This document defines a file format for the secure storage of passwords and related data. The format is designed according to current cryptographic best practices, and is believed to be secure, in the sense that without knowledge of the master passphrase, only a brute-force attack or a flaw in the underlying cryptographic algorithm will result in unauthorized access to the data. 1.1 Design Goals: The PasswordSafe database format is designed to be secure, extensible and platform-independent. 1.2 History: This specification is an evolution of previous formats. The main differences between version 3 of the format and previous versions are: 1.2.1. This version addresses a minor design flaw in previous versions of the PasswordSafe database format. 1.2.3. This version replaces the underlying cryptographic functions with more advanced versions. 1.2.4. This version allows the detection of a truncated or corrupted/tampered database. Meeting these goals is impossible without breaking compatibility: This format is NOT compatible with previous (major) versions. Note, however, that since the data stored in previous versions is a proper subset of the data described here, implementers may read a database written in an older version and store the result in the format described here. 2. Format: A V3 format PasswordSafe is structured as follows: TAG|SALT|ITER|H(P')|B1|B2|B3|B4|IV|HDR|R1|R2|...|Rn|EOF|HMAC Where: 2.1 TAG is the sequence of 4 ASCII characters "PWS3". This is to serve as a quick way for the application to identify the database as a PasswordSafe version 3 file. This tag has no cryptographic value. 2.2 SALT is a 256 bit random value, generated at file creation time. 2.3 P' is the "stretched key" generated from the user's passphrase and the SALT, as defined by the hash-function-based key stretching algorithm in [KEYSTRETCH] (Section 4.1), with SHA-256 [SHA256] as the hash function, and ITER iterations (at least 2048, i.e., t = 11). 2.4 ITER is the number of iterations on the hash function to calculate P', stored as a 32 bit little-endian value. This value is stored here in order to future-proof the file format against increases in processing power. 2.5 H(P') is SHA-256(P'), and is used to verify that the user has the correct passphrase. 2.6 B1 and B2 are two 128-bit blocks encrypted with Twofish [TWOFISH] using P' as the key, in ECB mode. These blocks contain the 256 bit random key K that is used to encrypt the actual records. (This has the property that there is no known or guessable information on the plaintext encrypted with the passphrase-derived key that allows an attacker to mount an attack that bypasses the key stretching algorithm.) 2.7 B3 and B4 are two 128-bit blocks encrypted with Twofish using P' as the key, in ECB mode. These blocks contain the 256 bit random key L that is used to calculate the HMAC (keyed-hash message authentication code) of the encrypted data. See description of EOF field below for more details. Implementation Note: K and L must NOT be related. 2.8 IV is the 128-bit random Initial Value for CBC mode. 2.9 All following records are encrypted using Twofish in CBC mode, with K as the encryption key. 2.9.1 HDR: The database header. The header consists of one or more typed fields (as defined in section 3.2), beginning with the Version type field, and terminated by the 'END' type field. The version number and END fields are mandatory. Aside from these two fields, no order is assumed on the field types. 2.9.2 R1..Rn: The actual database records. Each record consists of one or more typed fields (as defined in Section 3.2), terminated by the 'END' type field. The UUID, Title, and Password fields are mandatory. All non- mandatory fields may either be absent or have zero length. When a field is absent or zero-length, its default value shall be used. Aside from the 'END' field, no order is assumed on the field types. 2.10 EOF: The ASCII characters "PWS3-EOFPWS3-EOF" (note that this is exactly one block long), unencrypted. This is an implementation convenience to inform the application that the following bytes are to be processed differently. 2.11 HMAC: The 256-bit keyed-hash MAC, as described in RFC2104, with SHA- 256 as the underlying hash function. The value is calculated over all of the plaintext fields, that is, over all the data stored in all fields (starting from the version number in the header, ending with the last field of the last record). The key L, as stored in B3 and B4, is used as the hash key value. 3. Fields: Data in PasswordSafe is stored in typed fields. Each field consists of one or more blocks. The blocks are the blocks of the underlying encryption algorithm - 16 bytes long for Twofish. The first block contains the field length in the first 4 bytes (little-endian), followed by a one- byte type identifier. The rest of the block contains up to 11 bytes of record data. If the record has less than 11 bytes of data, the extra bytes are filled with random values. The type of a field also defines the data representation. 3.1 Data representations 3.1.1 UUID The UUID data type is 16 bytes long, as defined in RFC4122. Microsoft Windows has functions for this, and the RFC has a sample implementation. 3.1.2 Text Text is represented in UTF-8 encoding (as defined in RFC3629), with no byte order marker (BOM) and no end-of-string mark (e.g., null byte). Note that the latter isn't neccessary since the length of the field is provided explicitly. Note that ALL fields described as "text" are UTF-8 encoded unless explicitly stated otherwise. 3.1.3 Time Timestamps are stored as 32 bit, little endian, unsigned integers, representing the number of seconds since Midnight, January 1, 1970, GMT. (This is equivalent to the time_t type on Windows and POSIX. On the Macintosh, the value needs to be adjusted by the constant value 2082844800 to account for the different epoch of its time_t type.) Note that future versions of this format may allow time to be specifed in 64 bits as well. 3.2 Field types for the PasswordSafe database header: Currently Name Value Type Implemented Comments -------------------------------------------------------------------------- Version 0x00 2 bytes Y [1] UUID 0x01 UUID Y [2] Non-default preferences 0x02 Text Y [3] Tree Display Status 0x03 Text Y [4] Timestamp of last save 0x04 time_t Y [5] Who performed last save 0x05 Text Y [DEPRECATED 6] What performed last save 0x06 Text Y [7] Last saved by user 0x07 Text Y [8] Last saved on host 0x08 Text Y [9] Database Name 0x09 Text Y [10] Database Description 0x0a Text Y [11] Database Filters 0x0b Text Y [12] Reserved 0x0c - [13] Reserved 0x0d - [13] Reserved 0x0e - [13] Recently Used Entries 0x0f Text [14] Named Password Policies 0x10 Text [15] Empty Groups 0x11 Text [16] Reserved 0x12 Text [13] End of Entry 0xff [empty] Y [17] [1] The version number of the database format. For this version, the value is 0x0310 (stored in little-endian format, that is, 0x10, 0x03). PasswordSafe V3.01 introduced Format 0x0300 PasswordSafe V3.03 introduced Format 0x0301 PasswordSafe V3.09 introduced Format 0x0302 PasswordSafe V3.12 introduced Format 0x0303 PasswordSafe V3.13 introduced Format 0x0304 PasswordSafe V3.14 introduced Format 0x0305 PasswordSafe V3.19 introduced Format 0x0306 PasswordSafe V3.22 introduced Format 0x0307 PasswordSafe V3.25 introduced Format 0x0308 PasswordSafe V3.26 introduced Format 0x0309 PasswordSafe V3.28 introduced Format 0x030A PasswordSafe V3.29 introduced Format 0x030B PasswordSafe V3.29Y introduced Format 0x030C PasswordSafe V3.30 introduced Format 0x030D [2] A universally unique identifier is needed in order to synchronize databases across different machine. Representation is described in Section 3.1.1. [3] Non-default preferences are encoded in a string as follows: The string is of the form "X nn vv X nn vv..." Where X=[BIS] for boolean, integer and string respectively, nn is the numeric value of the enum, and vv is the value, {1 or 0} for bool, unsigned integer for int, and a delimited string for String. Only non-default values are stored. See PWSprefs.cpp for more details. Note: normally strings are delimited by the doublequote character. However, if this character is in the string value, an arbitrary character will be chosen to delimit the string. Version 0x0309 added database wide special symbols to the Non-default preferences (0x02). [4] If requested to be saved, this is a string of 1s and 0s indicating the expanded state of the tree display when the database was saved. This can be applied at database open time, if the user wishes, so that the tree is displayed as it was. Alternatively, it can be ignored and the tree displayed completely expanded or collapsed. Note that the mapping of the string to the display state is implementation-specific. Introduced in format 0x0301. [5] Representation is as described in Section 3.1.3. Note that prior to PasswordSafe 3.09, this field was mistakenly represented as an eight-byte hexadecimal ASCII string. Implementations SHOULD attempt to parse 8-byte long timestamps as a hexadecimal ASCII string representation of the timestamp value. [6] Text saved in the format: nnnnu..uh..h, where: nnnn = 4 hexadecimal digits giving length of following user name field u..u = user name h..h = host computer name Note: As of format 0x0302, this field is deprecated, and should be replaced by fields 0x07 and 0x08. In databases prior to format 0x0302, this field should be maintained. 0x0302 and later may either maintain this field in addition to fields 0x07 and 0x08, for backwards compatability, or not write this field. If both this field and 0x07, 0x08 exist, they MUST represent the same values. [7] Free form text giving the application that saved the database. For example, the Windows PasswordSafe application will use the text "Password Safe Vnn.mm", where nn and mm are the major and minor version numbers. The major version will contain only the significant digits whereas the minor version will be padded to the left with zeroes e.g. "Password Safe V3.02". [8] Text containing the username (e.g., login, userid, etc.) of the user who last saved the database, as determined by the appropriate operating-system dependent function. This field was introduced in format version 0x0302, as a replacement for field 0x05. See Comment [6]. [9] Text containing the hostname (e.g., machine name, hostid, etc.) of the machine on which the database was last saved, as determined by the appropriate operating-system dependent function. This field was introduced in format version 0x0302, as a replacement for field 0x05. See Comment [6]. [10] Database name. A logical name for a database which can be used by applications in place of the possibly lengthy filepath notion. Note that this field SHOULD be limited to what can be displayed in a single line. This field was introduced in format version 0x0302. [11] Database Description. A purely informative description concerning the purpose or other practical use of the database. This field was introduced in format version 0x0302. [12] Specfic filters for this database. This is the text equivalent to the XML export of the filters as defined by the filter schema. The text 'image' has no 'print formatting' e.g. tabs and carraige return/line feeds, since XML processing does not require this. This field was introduced in format version 0x0305. [13] Values marked 'Reserved' are know to have been used by customized versions of PasswordSafe. To ensure compatability between versions, use of these values should be avoided. [14] A list of the UUIDs (32 hex character representation of the 16 byte field) of the recently used entries, prefixed by a 2 hex character representation of the number of these entries (right justified and left filled with zeroes). The size of the number of entries field gives a maximum number of entries of 255, however the GUI may impose further restrictions e.g. Windows MFC UI limits this to 25. The first entry is the most recent entry accessed. This field was introduced in format version 0x0307. [15] This field allows multiple Password Policies per database. The format is: "NN{LLxxx...xxxffffnnnllluuudddsssMMSSS...SSS}" where: NN = 2 hexadecimal digits giving number of password policies in field (max. 255). Each entry is of the form: LL = 2 hexadecimal digits giving length of the policy name in bytes xxx...xxx = The policy name (maximum 255 bytes) ffff = 4 hexadecimal digits representing the following flags UseLowercase 0x8000 UseUppercase 0x4000 UseDigits 0x2000 UseSymbols 0x1000 UseHexDigits 0x0800 (if set, then no other flags can be set) UseEasyVision 0x0400 MakePronounceable 0x0200 Unused 0x01ff nnn = 3 hexadecimal digits password total length lll = 3 hexadecimal digits password minimum number of lowercase characters uuu = 3 hexadecimal digits password minimum number of uppercase characters ddd = 3 hexadecimal digits password minimum number of digit characters sss = 3 hexadecimal digits password minimum number of symbol characters MM = 2 hexadecimal digits giving the length in bytes of allowed special symbols, or zero to specify the database default special symbol set. SSS...SSS = List of allowed symbols in this policy (maximum 255 bytes) This field was introduced in format version 0x030A. [16] This fields contains the name of an empty group that cannot be constructed from entries within the database. Unlike other header fields, this field can appear multiple times. This field was introduced in format version 0x030B. [17] An explicit end of entry field is useful for supporting new fields without breaking backwards compatability. 3.3 Field types for database Records: Currently Name Value Type Implemented Comments -------------------------------------------------------------------------- UUID 0x01 UUID Y [1] Group 0x02 Text Y [2] Title 0x03 Text Y Username 0x04 Text Y Notes 0x05 Text Y Password 0x06 Text Y [3,4] Creation Time 0x07 time_t Y [5] Password Modification Time 0x08 time_t Y [5] Last Access Time 0x09 time_t Y [5,6] Password Expiry Time 0x0a time_t Y [5,7] *RESERVED* 0x0b 4 bytes - [8] Last Modification Time 0x0c time_t Y [5,9] URL 0x0d Text Y [10] Autotype 0x0e Text Y [11] Password History 0x0f Text Y [12] Password Policy 0x10 Text Y [13] Password Expiry Interval 0x11 2 bytes Y [14] Run Command 0x12 Text Y Double-Click Action 0x13 2 bytes Y [15] EMail address 0x14 Text Y [16] Protected Entry 0x15 1 byte Y [17] Own symbols for password 0x16 Text Y [18] Shift Double-Click Action 0x17 2 bytes Y [15] Password Policy Name 0x18 Text Y [19] Entry keyboard shortcut 0x19 4 bytes Y [20] End of Entry 0xff [empty] Y [21] [1] Per-record UUID to assist in sync, merge, etc. Representation is as described in Section 3.1.1. [2] The "Group" supports displaying the entries in a tree-like manner. Groups can be hierarchical, with elements separated by a period, supporting groups such as "Finance.credit cards.Visa", "Finance.credit cards.Mastercard", Finance.bank.web access", etc. Dots entered by the user should be "escaped" by the application. [3] If the entry is an alias, the password will be saved in a special form of "[[uuidstr]]", where "uuidstr" is a 32-character representation of the alias' associated base entry's UUID (field type 0x01). This representation is the same as the standard 36-character string representation as defined in RFC4122 but with the four hyphens removed. If an entry with this UUID is not in the database, this is treated just as an 'unusual' password. The alias will only use its base's password entry when copying it to the clipboard or during Autotype. [4] If the entry is a shortcut, the password will be saved in a special form of "[~uuidstr~]", where "uuidstr" is a 32-character representation of the shortcut's associated base entry's UUID (field type 0x01). This representation is the same as the standard 36-character string representation as defined in RFC4122 but with the four hyphens removed. If an entry with this UUID is not in the database, this is treated just as an 'unusual' password. The shortcut will use all its base's data when used in any action. It has no fields of its own. [5] Representation is as described in Section 3.1.3. [6] This will be updated whenever any part of this entry is accessed i.e., to copy its username, password or notes to the clipboard; to perform autotype or to browse to url. [7] This will allow the user to enter an expiry date for an entry. The application can then prompt the user about passwords that need to be changed. A value of zero means "forever". [8] Although earmarked for Password Policy, the coding in versions prior to V3.12 does not correctly handle the presence of this field. For this reason, this value cannot be used for any future V3 field without causing a potential issue when a user opens a V3.12 or later database with program version V3.11 or earlier. See note [14]. [9] This is the time that any field of the record was modified, useful for merging databases. [10] The URL will be passed to the shell when the user chooses the "Browse to" action for this entry. In version 2 of the format, this was extracted from the Notes field. By placing it in a separate field, we are no longer restricted to a URL - any action that may be executed by the shell may be specified here. [11] The text to be 'typed' by PasswordSafe upon the "Perform Autotype" action maybe specified here. If unspecified, the default value of 'username, tab, password, tab, enter' is used. In version 2 of the format, this was extracted from the Notes field. Several codes are recognized here, e.g, '%p' is replaced by the record's password. See the user documentation for the complete list of codes. The replacement is done by the application at runtime, and is not stored in the database. [12] Password History is an optional record. If it exists, it stores the creation times and values of the last few passwords used in the current entry, in the following format: "fmmnnTLPTLP...TLP" where: f = {0,1} if password history is on/off mm = 2 hexadecimal digits max size of history list (i.e. max = 255) nn = 2 hexadecimal digits current size of history list T = Time password was set (time_t written out in %08x) L = 4 hexadecimal digit password length (in TCHAR) P = Password No history being kept for a record can be represented either by the lack of the PWH field (preferred), or by a header of _T("00000"): flag = 0, max = 00, num = 00 Note that 0aabb, where bb <= aa, is possible if password history was enabled in the past and has then been disabled but the history hasn't been cleared. [13] This field allows a specific Password Policy per entry. This field is mutually exclusive with the policy name field [0x18]. The format is: "ffffnnnllluuudddsss" where: ffff = 4 hexadecimal digits representing the following flags UseLowercase 0x8000 UseUppercase 0x4000 UseDigits 0x2000 UseSymbols 0x1000 UseHexDigits 0x0800 (if set, then no other flags can be set) UseEasyVision 0x0400 MakePronounceable 0x0200 Unused 0x01ff nnn = 3 hexadecimal digits password total length lll = 3 hexadecimal digits password minimum number of lowercase characters uuu = 3 hexadecimal digits password minimum number of uppercase characters ddd = 3 hexadecimal digits password minimum number of digit characters sss = 3 hexadecimal digits password minimum number of symbol characters [14] Password Expiry Interval, in days, before this password expires. Once set, this value is used when the password is first generated and thereafter whenever the password is changed, until this value is unset. Valid values are 1-3650 corresponding to up to approximately 10 years. A value of zero is equivalent to this field not being set. [15] A two byte field contain the value of the Double-Click Action and Shift Double-Click Action'preference value' (0xff means use the current Application default): Current 'preference values' are: CopyPassword 0 ViewEdit 1 AutoType 2 Browse 3 CopyNotes 4 CopyUsername 5 CopyPasswordMinimize 6 BrowsePlus 7 Run Command 8 Send email 9 [16] Separate Email address field as per RFC 2368 (without the 'mailto:' prefix. This field was introduced in version 0x0306 (PasswordSafe V3.19). [17] Entry is protected, i.e., the entry cannot be changed or deleted while this field is set. This field was introduced in version 0x0308 (PasswordSafe V3.25). This a single byte. An absent field or a zero valued field means that the entry is not protected. Any non-zero value means that the entry is protected. [18] Each entry can now specify its own set of allowed special symbols for password generation. This overrides the default set and any database specific set. This field is mutually exclusive with the policy name field [0x18]. This was introduced in version 0x0309 (PasswordSafe V3.26). [19] Each entry can now specify the name of a Password Policy saved in the database header for password generation. This field is mutually exclusive with the specific policy field [0x10] and with the Own symbols for password field [0x16]. This was introduced in version 0x030A (PasswordSafe V3.28). [20] Entry keyboard shortcut. Format is: Bytes 0-1: Virtual KeyCode for the character (Windows only uses byte 1) Byte 2 : zero Byte 3 : Keyboard Modifiers - a bitwise OR of any valid combination of: PWS_HOTKEYF_ALT 0x01 PWS_HOTKEYF_CONTROL 0x02 PWS_HOTKEYF_SHIFT 0x04 PWS_HOTKEYF_EXT 0x08 PWS_HOTKEYF_META 0x10 (not supported by Windows) PWS_HOTKEYF_WIN 0x20 (not supported by Windows) PWS_HOTKEYF_CMD 0x40 (not supported by Windows) This was introduced in version 0x030D (PasswordSafe V3.30). [21] An explicit end of entry field is useful for supporting new fields without breaking backwards compatability. 4. Extensibility 4.1 Forward compatability: Implementations of this format SHOULD NOT discard or report an error when encountering a field of an unknown type. Rather, the field(s) type and data should be read, and preserved when the database is saved. 4.2 Field type identifiers: This document specifies the field type identifiers for the current version of the format. Compliant implementations MUST support the mandatory fields, and SHOULD support the other fields described herein. Future versions of the format may specify other type identifiers. 4.2.1 Application-unique type identifiers: The type identifiers 0xc0-0xdf are available for application developers on a first-come first-serve basis. Application developers interested in reserving a type identifier for their application should contact the maintainer of this document (Currently the PasswordSafe project administrator at SourceForge). 4.2.2 Application-specific type identifiers: The type identifiers 0xe0-0xfe are reserved for implementation-specific purposes, and will NOT be specified in this or future versions of the format description. 4.2.3 All unassigned identifiers except as listed in the previous two subsections are reserved, and should not be used by other implementations of this format specification in the interest of interoperablity. 5. References: [TWOFISH] http://www.schneier.com/paper-twofish-paper.html [SHA256] http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf [KEYSTRETCH] http://www.schneier.com/paper-low-entropy.pdf End of Format description. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/docs/formatV322.txt0000644000175000017500000005530600000000000020074 0ustar00mdeslaurmdeslaur00000000000000PasswordSafe database format description version 3.22 ----------------------------------------------------- Copyright (c) 2003-2011 Rony Shapiro . All rights reserved. Use of the code is allowed under the Artistic License terms, as specified in the LICENSE file distributed with this code, or available from http://www.opensource.org/licenses/artistic-license-2.0.php 1. Introduction: This document defines a file format for the secure storage of passwords and related data. The format is designed according to current cryptographic best practices, and is believed to be secure, in the sense that without knowledge of the master passphrase, only a brute-force attack or a flaw in the underlying cryptographic algorithm will result in unauthorized access to the data. 1.1 Design Goals: The PasswordSafe database format is designed to be secure, extensible and platform-independent. 1.2 History: This specification is an evolution of previous formats. The main differences between version 3 of the format and previous versions are: 1.2.1. This version addresses a minor design flaw in previous versions of the PasswordSafe database format. 1.2.3. This version replaces the underlying cryptographic functions with more advanced versions. 1.2.4. This version allows the detection of a truncated or corrupted/tampered database. Meeting these goals is impossible without breaking compatibility: This format is NOT compatible with previous (major) versions. Note, however, that since the data stored in previous versions is a proper subset of the data described here, implementers may read a database written in an older version and store the result in the format described here. 2. Format: A V3 format PasswordSafe is structured as follows: TAG|SALT|ITER|H(P')|B1|B2|B3|B4|IV|HDR|R1|R2|...|Rn|EOF|HMAC Where: 2.1 TAG is the sequence of 4 ASCII characters "PWS3". This is to serve as a quick way for the application to identify the database as a PasswordSafe version 3 file. This tag has no cryptographic value. 2.1 SALT is a 256 bit random value, generated at file creation time. 2.3 P' is the "stretched key" generated from the user's passphrase and the SALT, as defined in by the hash-function-based key stretching algorithm in [KEYSTRETCH] (Section 4.1), with SHA-256 [SHA256] as the hash function, and ITER iterations (at least 2048, i.e., t = 11). 2.4 ITER is the number of iterations on the hash function to calculate P', stored as a 32 bit little-endian value. This value is stored here in order to future-proof the file format against increases in processing power. 2.5 H(P') is SHA-256(P'), and is used to verify that the user has the correct passphrase. 2.6 B1 and B2 are two 128-bit blocks encrypted with Twofish [TWOFISH] using P' as the key, in ECB mode. These blocks contain the 256 bit random key K that is used to encrypt the actual records. (This has the property that there is no known or guessable information on the plaintext encrypted with the passphrase-derived key that allows an attacker to mount an attack that bypasses the key stretching algorithm.) 2.7 B3 and B4 are two 128-bit blocks encrypted with Twofish using P' as the key, in ECB mode. These blocks contain the 256 bit random key L that is used to calculate the HMAC (keyed-hash message authentication code) of the encrypted data. See description of EOF field below for more details. Implementation Note: K and L must NOT be related. 2.8 IV is the 128-bit random Initial Value for CBC mode. 2.9 All following records are encrypted using Twofish in CBC mode, with K as the encryption key. 2.9.1 HDR: The database header. The header consists of one or more typed fields (as defined in section 3.2), terminated by the 'END' type field. The version number field is mandatory. Aside from the 'END' field, no order is assumed on the field types. 2.9.2 R1..Rn: The actual database records. Each record consists of one or more typed fields (as defined in Section 3.2), terminated by the 'END' type field. The UUID, Title, and Password fields are mandatory. All non- mandatory fields may either be absent or have zero length. When a field is absent or zero-length, its default value shall be used. Aside from the 'END' field, no order is assumed on the field types. 2.10 EOF: The ASCII characters "PWS3-EOFPWS3-EOF" (note that this is exactly one block long), unencrypted. This is an implementation convenience to inform the application that the following bytes are to be processed differently. 2.11 HMAC: The 256-bit keyed-hash MAC, as described in RFC2104, with SHA- 256 as the underlying hash function. The value is calculated over all of the plaintext fields, that is, over all the data stored in all fields (starting from the version number in the header, ending with the last field of the last record). The key L, as stored in B3 and B4, is used as the hash key value. 3. Fields: Data in PasswordSafe is stored in typed fields. Each field consists of one or more blocks. The blocks are the blocks of the underlying encryption algorithm - 16 bytes long for Twofish. The first block contains the field length in the first 4 bytes (little-endian), followed by a one- byte type identifier. The rest of the block contains up to 11 bytes of record data. If the record has less than 11 bytes of data, the extra bytes are filled with random values. The type of a field also defines the data representation. 3.1 Data representations 3.1.1 UUID The UUID data type is 16 bytes long, as defined in RFC4122. Microsoft Windows has functions for this, and the RFC has a sample implementation. 3.1.2 Text Text is represented in UTF-8 encoding (as defined in RFC3629), with no byte order marker (BOM) and no end-of-string mark (e.g., null byte). Note that the latter isn't neccessary since the length of the field is provided explicitly. Note that ALL fields described as "text" are UTF-8 encoded unless explicitly stated otherwise. 3.1.3 Time Timestamps are stored as 32 bit, little endian, unsigned integers, representing the number of seconds since Midnight, January 1, 1970, GMT. (This is equivalent to the time_t type on Windows and POSIX. On the Macintosh, the value needs to be adjusted by the constant value 2082844800 to account for the different epoch of its time_t type.) Note that future versions of this format may allow time to be specifed in 64 bits as well. 3.2 Field types for the PasswordSafe database header: Currently Name Value Type Implemented Comments -------------------------------------------------------------------------- Version 0x00 2 bytes Y [1] UUID 0x01 UUID Y [2] Non-default preferences 0x02 Text Y [3] Tree Display Status 0x03 Text Y [4] Timestamp of last save 0x04 time_t Y [5] Who performed last save 0x05 Text Y [DEPRECATED 6] What performed last save 0x06 Text Y [7] Last saved by user 0x07 Text Y [8] Last saved on host 0x08 Text Y [9] Database Name 0x09 Text Y [10] Database Description 0x0a Text Y [11] Database Filters 0x0b Text Y [12] Reserved 0x0c - [13] Reserved 0x0d - [13] Reserved 0x0e - [13] Recently Used Entries 0x0f - [14] End of Entry 0xff [empty] Y [15] [1] The version number of the database format. For this version, the value is 0x0307 (stored in little-endian format, that is, 0x07, 0x03). PasswordSafe V3.01 introduced Format 0x0300 PasswordSafe V3.03 introduced Format 0x0301 PasswordSafe V3.09 introduced Format 0x0302 PasswordSafe V3.12 introduced Format 0x0303 PasswordSafe V3.13 introduced Format 0x0304 PasswordSafe V3.14 introduced Format 0x0305 PasswordSafe V3.19 introduced Format 0x0306 PasswordSafe V3.22 introduced Format 0x0307 PasswordSafe V3.25 introduced Format 0x0308 PasswordSafe V3.26 introduced Format 0x0309 [2] A universally unique identifier is needed in order to synchronize databases, e.g., between a handheld pocketPC device and a PC. Representation is as described in Section 3.1.1. [3] Non-default preferences are encoded in a string as follows: The string is of the form "X nn vv X nn vv..." Where X=[BIS] for binary, integer and string respectively, nn is the numeric value of the enum, and vv is the value, {1 or 0} for bool, unsigned integer for int, and a delimited string for String. Only non-default values are stored. See PWSprefs.cpp for more details. Note: normally strings are delimited by the doublequote character. However, if this character is in the string value, an arbitrary character will be chosen to delimit the string. Version 0x0309 added database wide special symbols to the Non-default preferences (0x02). [4] If requested to be saved, this is a string of 1s and 0s indicating the expanded state of the tree display when the database was saved. This can be applied at database open time, if the user wishes, so that the tree is displayed as it was. Alternatively, it can be ignored and the tree displayed completely expanded or collapsed. Note that the mapping of the string to the display state is implementation-specific. Introduced in format 0x0301. [5] Representation is as described in Section 3.1.3. Note that prior to PasswordSafe 3.09, this field was mistakenly represented as an eight-byte hexadecimal ASCII string. Implementations SHOULD attempt to parse 8-byte long timestamps as a hexadecimal ASCII string representation of the timestamp value. [6] Text saved in the format: nnnnu..uh..h, where: nnnn = 4 hexadecimal digits giving length of following user name field u..u = user name h..h = host computer name Note: As of format 0x0302, this field is deprecated, and should be replaced by fields 0x07 and 0x08. In databases prior to format 0x0302, this field should be maintained. 0x0302 and later may either maintain this field in addition to fields 0x07 and 0x08, for backwards compatability, or not write this field. If both this field and 0x07, 0x08 exist, they MUST represent the same values. [7] Free form text giving the application that saved the database. For example, the Windows PasswordSafe application will use the text "Password Safe Vnn.mm", where nn and mm are the major and minor version numbers. The major version will contain only the significant digits whereas the minor version will be padded to the left with zeroes e.g. "Password Safe V3.02". [8] Text containing the username (e.g., login, userid, etc.) of the user who last saved the database, as determined by the appropriate operating-system dependent function. This field was introduced in format version 0x0302, as a replacement for field 0x05. See Comment [6]. [9] Text containing the hostname (e.g., machine name, hostid, etc.) of the machine on which the database was last saved, as determined by the appropriate operating-system dependent function. This field was introduced in format version 0x0302, as a replacement for field 0x05. See Comment [6]. [10] Database name. A logical name for a database which can be used by applications in place of the possibly lengthy filepath notion. Note that this field SHOULD be limited to what can be displayed in a single line. This field was introduced in format version 0x0302. [11] Database Description. A purely informative description concerning the purpose or other practical use of the database. This field was introduced in format version 0x0302. [12] Specfic filters for this database. This is the text equivalent to the XML export of the filters as defined by the filter schema. The text 'image' has no 'print formatting' e.g. tabs and carraige return/line feeds, since XML processing does not require this. This field was introduced in format version 0x0305. [13] Values marked 'Reserved' are know to have been used by customized versions of PasswordSafe. To ensure compatability beweeen versions, use of these values should be avoided. [14] A list of the UUIds (32 hex character representation of the 16 byte field) of the recently used entries, prefixed by a 2 hex character representation of the number of these entries (right justified and left filled with zeroes). The size of the number of entries field gives a maximum number of entries of 255, however the GUI may impose further restrictions e.g. Windows MFC UI limits this to 25. The first entry is the most recent entry accessed. This field was introduced in format version 0x0307. [15] An explicit end of entry field is useful for supporting new fields without breaking backwards compatability. 3.3 Field types for database Records: Currently Name Value Type Implemented Comments -------------------------------------------------------------------------- UUID 0x01 UUID Y [1] Group 0x02 Text Y [2] Title 0x03 Text Y Username 0x04 Text Y Notes 0x05 Text Y Password 0x06 Text Y [3,4] Creation Time 0x07 time_t Y [5] Password Modification Time 0x08 time_t Y [5] Last Access Time 0x09 time_t Y [5,6] Password Expiry Time 0x0a time_t Y [5,7] *RESERVED* 0x0b 4 bytes - [8] Last Modification Time 0x0c time_t Y [5,9] URL 0x0d Text Y [10] Autotype 0x0e Text Y [11] Password History 0x0f Text Y [12] Password Policy 0x10 Text Y [13] Password Expiry Interval 0x11 2 bytes Y [14] Run Command 0x12 Text Y Double-Click Action 0x13 2 bytes Y [15] EMail address 0x14 Text Y [16] Protected Entry 0x15 1 byte Y [17] Own symbols for password 0x16 Text Y [18] End of Entry 0xff [empty] Y [19] [1] Per-record UUID to assist in sync, merge, etc. Representation is as described in Section 3.1.1. [2] The "Group" supports displaying the entries in a tree-like manner. Groups can be hierarchical, with elements separated by a period, supporting groups such as "Finance.credit cards.Visa", "Finance.credit cards.Mastercard", Finance.bank.web access", etc. Dots entered by the user should be "escaped" by the application. [3] If the entry is an alias, the password will be saved in a special form of "[[uuidstr]]", where "uuidstr" is a 32-character representation of the alias' associated base entry's UUID (field type 0x01). This representation is the same as the standard 36-character string representation as defined in RFC4122 but with the four hyphens removed. If an entry with this UUID is not in the database, this is treated just as an 'unusual' password. The alias will only use its base's password entry when copying it to the clipboard or during Autotype. [4] If the entry is a shortcut, the password will be saved in a special form of "[~uuidstr~]", where "uuidstr" is a 32-character representation of the shortcut's associated base entry's UUID (field type 0x01). This representation is the same as the standard 36-character string representation as defined in RFC4122 but with the four hyphens removed. If an entry with this UUID is not in the database, this is treated just as an 'unusual' password. The shortcut will use all its base's data when used in any action. It has no fields of its own. [5] Representation is as described in Section 3.1.3. [6] This will be updated whenever any part of this entry is accessed i.e., to copy its username, password or notes to the clipboard; to perform autotype or to browse to url. [7] This will allow the user to enter an expiry date for an entry. The application can then prompt the user about passwords that need to be changed. A value of zero means "forever". [8] Although earmarked for Password Policy, the coding in versions prior to V3.12 does not correctly handle the presence of this field. For this reason, this value cannot be used for any future V3 field without causing a potential issue when a user opens a V3.12 or later database with program version V3.11 or earlier. See note [14]. [9] This is the time that any field of the record was modified, useful for merging databases. [10] The URL will be passed to the shell when the user chooses the "Browse to" action for this entry. In version 2 of the format, this was extracted from the Notes field. By placing it in a separate field, we are no longer restricted to a URL - any action that may be executed by the shell may be specified here. [11] The text to be 'typed' by PasswordSafe upon the "Perform Autotype" action maybe specified here. If unspecified, the default value of 'username, tab, password, tab, enter' is used. In version 2 of the format, this was extracted from the Notes field. Several codes are recognized here, e.g, '%p' is replaced by the record's password. See the user documentation for the complete list of codes. The replacement is done by the application at runtime, and is not stored in the database. [12] Password History is an optional record. If it exists, it stores the creation times and values of the last few passwords used in the current entry, in the following format: "fmmnnTLPTLP...TLP" where: f = {0,1} if password history is on/off mm = 2 hexadecimal digits max size of history list (i.e. max = 255) nn = 2 hexadecimal digits current size of history list T = Time password was set (time_t written out in %08x) L = 4 hexadecimal digit password length (in TCHAR) P = Password No history being kept for a record can be represented either by the lack of the PWH field (preferred), or by a header of _T("00000"): flag = 0, max = 00, num = 00 Note that 0aabb, where bb <= aa, is possible if password history was enabled in the past and has then been disabled but the history hasn't been cleared. [13] This field allows a specific Password Policy per entry. The format is: "ffffnnnllluuudddsss" where: ffff = 4 hexadecimal digits representing the following flags UseLowercase 0x8000 - can have a minimum length UseUppercase 0x4000 - can have a minimum length UseDigits 0x2000 - can have a minimum length UseSymbols 0x1000 - can have a minimum length UseHexDigits 0x0800 (if set, then no other flags can be set) UseEasyVision 0x0400 MakePronounceable 0x0200 Unused 0x01ff nnn = 3 hexadecimal digits password total length lll = 3 hexadecimal digits password minimum number of lowercase characters uuu = 3 hexadecimal digits password minimum number of uppercase characters ddd = 3 hexadecimal digits password minimum number of digit characters sss = 3 hexadecimal digits password minimum number of symbol characters [14] Password Expiry Interval, in days, before this password expires. Once set, this value is used when the password is first generated and thereafter whenever the password is changed, until this value is unset. Valid values are 1-3650 corresponding to up to approximately 10 years. A value of zero is equivalent to this field not being set. [15] A two byte field contain the value of the Double-Click Action 'preference value' (0xff means use the current Application default): Current 'preference values' are: CopyPassword 0 ViewEdit 1 AutoType 2 Browse 3 CopyNotes 4 CopyUsername 5 CopyPasswordMinimize 6 BrowsePlus 7 [16] Separate Email address field as per RFC 2368 (without the 'mailto:' prefix. This field was introduced in version 0x0306 (PasswordSafe V3.19). [17] Entry is protected i.e. the entry cannot be changed until the user unsets this field. Effectively a read-only entry in a read-write database. This field was introduced in version 0x0308 (PasswordSafe V3.25). This a single character. An absent field or a zero valued field means that the entry is not protected. Any non-zero value is taken as meaning that the entry is protected. [18] Each entry can now specify it on set of allowed special symbols for password generation. This overrides the default set and any database specific set. This was introduced in version 0x0309 (PasswordSafe V3.26). [19] An explicit end of entry field is useful for supporting new fields without breaking backwards compatability. 4. Extensibility 4.1 Forward compatability: Implementations of this format SHOULD NOT discard or report an error when encountering a field of an unknown type. Rather, the field(s) type and data should be read, and perserved when the database is saved. 4.2 Field type identifiers: This document specifies the field type identifiers for the current version of the format. Compliant implementations MUST support the mandatory fields, and SHOULD support the other fields described herein. Future versions of the format may specify other type identifiers. 4.2.1 Application-unique type identifiers: The type identifiers 0xc0-0xdf are available for application developers on a first-come first-serve basis. Application developers interested in reserving a type identifier for their application should contact the maintainer of this document (Currently the PasswordSafe project administrator at SourceForge). 4.2.2 Application-specific type identifiers: The type identifiers 0xe0-0xfe are reserved for implementation-specific purposes, and will NOT be specified in this or future versions of the format description. 4.2.3 All unassigned identifiers except as listed in the previous two subsections are reserved, and should not be used by other implementations of this format specification in the interest of interoperablity. 5. References: [TWOFISH] http://www.schneier.com/paper-twofish-paper.html [SHA256] http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf [KEYSTRETCH] http://www.schneier.com/paper-low-entropy.pdf End of Format description. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1414277147.0 pasaffe-0.57/docs/interopability.txt0000644000175000017500000000347200000000000021262 0ustar00mdeslaurmdeslaur00000000000000Here are some current issues with interopability: 1- Line terminators in notes Password Safe for Windows v3.31, Password Gorilla v1.5.3.6.3 on Ubuntu 13.04 and PasswdSafe for Android create notes with CRLF line terminators, while Pasaffe creates them with LF line terminators. This doesn't seem to create an issue, but perhaps Pasaffe should make sure they are CRLF in the database, and migrate existing entries on import. 2- Missing passwords Pasaffe used to allow creating entries with no password fields. Password Gorilla v1.5.3.6.3 on Ubuntu 13.04, and possibly other versions, has no trouble with missing password fields, but Password Safe for Windows v3.31, and possibly other versions, gives an error dialog when opening the database and proceeds to set a default password for all entries with no password fields. Password Gorilla v1.5.3.7, and possibly other versions, allows creating entries with no password fields. Password Safe for Windows v3.26, and possibly other versions, will not create en entry with no password, it will display an error message if the user doesn't use the password field. PasswdSafe for Android will not create an entry with no password. 3- Missing usernames Password Gorilla v1.5.3.7, and possibly other versions, allows creating entries with no username fields. Password Safe for Windows v3.26, and possibly other versions, creates entries with no usernames with a username field composed of a blank string. PasswdSafe for Android will create entries with no username field. 3- Password Modification Time When Password Gorilla doesn't save a password entry, it also doesn't save a Password Modification Time field. PasswdSafe on Android doesn't seem to save a Password Modification Time field. 4- Last Modification Time PasswdSafe on Android doesn't seem to save a Last Modification Time field. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3600426 pasaffe-0.57/help/0000775000175000017500000000000000000000000015457 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/help/C/0000775000175000017500000000000000000000000015641 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978148.0 pasaffe-0.57/help/C/delete-entry.page0000644000175000017500000000177100000000000021104 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com 2011 How to delete an entry. Deleting an entry

You may remove an entry that you no longer wish to have in the database.

Deleting an entry in <app>Pasaffe</app>

In the left section, select the entry you wish to delete.

Click the icon that looks like a minus sign, or select EditDelete.

Click on Yes to confirm.

Removing an entry is permanent and cannot be undone. If you wish to cancel, click on No when asked for confirmation.

././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/help/C/figures/0000775000175000017500000000000000000000000017305 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/help/C/figures/icon.png0000644000175000017500000000116400000000000020743 0ustar00mdeslaurmdeslaur00000000000000PNG  IHDRasRGBbKGD pHYs B(xtIME *IDAT8˅1kTQ3snK XHH`JYbJ.?2BPl,I V6YBh &/Zܷ U6bKmoow~Ho8IENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978106.0 pasaffe-0.57/help/C/index.page0000644000175000017500000000232400000000000017605 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com <!-- This shows on the page in title font --> <!-- the icon only shows when installed --> <media type="image" mime="image/png" src="figures/icon.png" /> <app>Pasaffe</app> Password Manager

Welcome to the Pasaffe help guide!

Pasaffe is a simple to use password manager. You can use it to store usernames and passwords, and encrypt them with a single Master Password.

Pasaffe's main window is divided into two sections. The section on the left displays a list of your entries, and the one on the right displays the currently selected entry's contents.

To get started using Pasaffe, visit the Adding a new entry page.

Contents
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978212.0 pasaffe-0.57/help/C/legal.xml0000644000175000017500000000044200000000000017445 0ustar00mdeslaurmdeslaur00000000000000

This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978153.0 pasaffe-0.57/help/C/new-entry.page0000644000175000017500000000324500000000000020431 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com 2011 How to create a new entry to store a username and password. Adding a new entry

To create a new entry, click on the icon that looks like a plus sign. If you prefer, you may also select EditAdd Entry. A window will appear asking for information.

Adding a new entry in <app>Pasaffe</app>

Open the Edit entry window by clicking the icon that looks like a plus sign, or selecting EditAdd Entry.

Enter a name for the new entry into the 'Title:' field.

Select a folder where this new entry will be located in the 'Folder:' field.

Enter a URL for the entry into the 'URL:' field.

Enter the entry's Username into the 'Username:' field.

Enter the entry's Password into the 'Password:' field. You may also click the button to generate a random password.

Enter any other information about the entry into the 'Notes:' field.

Once you are done entering the information, click OK

Only the 'Title:' field is required. All other fields are optional, and can be left blank.

././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978155.0 pasaffe-0.57/help/C/preferences.page0000644000175000017500000000364500000000000021006 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com 2011 How to adjust Pasaffe user preferences and settings. Setting preferences

To adjust Pasaffe settings, click on EditPreferences. A window will appear with some options. When you are done modifying them, click OK.

Here are some of the settings that are available:

Automatically save changes: When checked, Pasaffe will save the database immediately after each change. By default, changes are only saved when specifically requested by the user, so unwanted changes can be cancelled.

Display secrets by default: When checked, Pasaffe will always display secrets, such as passwords, in the right section of the main window.

Only passwords are secret: When checked, only the password field will be considered to be a secret that should be hidden by default. Unchecking this causes the other fields to be hidden by default also.

Lock database after (5) minutes: When checked, the Pasaffe window will lock the database and ask for the Master Password again after a certain amount of minutes have passed. The amount of minutes can be adjusted.

Passwords are (12) characters long: Length of the passwords generated by the random password button.

Folders: When Pasaffe is opened, folders can be expanded by default, collapsed by default, or the folders can be remembered from the last time the database was saved.

././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978162.0 pasaffe-0.57/help/C/show-secrets.page0000644000175000017500000000216400000000000021126 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com 2011 How to display an entry's secrets. Showing an entry's secrets

Secret information, such as passwords, aren't displayed by default in the right section of the Pasaffe window.

Displaying an entry's secrets in <app>Pasaffe</app>

In the left section, select the entry you wish to display.

Click the icon that looks like a set of keys, or select ViewDisplay Secrets.

Click once more to hide the secrets once again.

You can make Pasaffe display secrets by default in the preferences dialog. See the Setting preferences page.

././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/help/fr/0000775000175000017500000000000000000000000016066 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978164.0 pasaffe-0.57/help/fr/delete-entry.page0000644000175000017500000000220000000000000021315 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com Comment supprimer une entrée. Suppression d'une entrée

Vous pouvez supprimer une entrée de la base de données.

Suppression d'une entrée dans <app>Pasaffe</app>

Dans la section de gauche, sélectionner l'entrée à supprimer.

Cliquer sur l'icône qui ressemble à un moins, ou sélectionner ÉditerSupprimer.

Cliquer sur Oui pour confirmer.

La suppression d'une entrée est une opération permanente. Si vous voulez annuler l'opération, cliquer sur Non à la demande de confirmation.

././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/help/fr/figures/0000775000175000017500000000000000000000000017532 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/help/fr/figures/icon.png0000644000175000017500000000116400000000000021170 0ustar00mdeslaurmdeslaur00000000000000PNG  IHDRasRGBbKGD pHYs B(xtIME *IDAT8˅1kTQ3snK XHH`JYbJ.?2BPl,I V6YBh &/Zܷ U6bKmoow~Ho8IENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978170.0 pasaffe-0.57/help/fr/index.page0000644000175000017500000000250400000000000020032 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com <!-- This shows on the page in title font --> <!-- the icon only shows when installed --> <media type="image" mime="image/png" src="figures/icon.png" /> Gestionnaire de mots de passe <app>Pasaffe</app>

Bienvenue dans le manuel d'aide de Pasaffe !

Pasaffe est un gestionnaire de mots de passe facile à utiliser. Vous pouvez l'utiliser pour entreposer des noms d'utilisateur et des mots de passe, et les chiffrer avec un mot de passe principal.

La fenêtre principale de Pasaffe est divisée en deux sections. La section de gauche affiche une liste d'entrées, et celle de droite affiche les données de l'entrée sélectionnée.

Pour démarrer avec Pasaffe, visitez la page Création d'une nouvelle entrée.

Contenu
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978387.0 pasaffe-0.57/help/fr/legal.xml0000644000175000017500000000053000000000000017670 0ustar00mdeslaurmdeslaur00000000000000

Ce travail est publié sous la Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé.

././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978176.0 pasaffe-0.57/help/fr/new-entry.page0000644000175000017500000000403600000000000020655 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com Comment créer une nouvelle entrée pour entreposer un nom d'utilisateur et un mot de passe. Création d'une nouvelle entrée

Pour créer une nouvelle entrée, cliquer sur l'icône qui ressemble à un plus. Si vous préférez, vous pouvez aussi sélectionner ÉditerAjouter une entrée. Une fenêtre apparaîtera pour entrer les informations.

Ajout d'une nouvelle entrée dans <app>Pasaffe</app>

Ouvrir la fenêtre Modifier une entrée en cliquant sur l'icône qui ressemble à un plus, ou en sélectionnant ÉditerAjouter une entrée.

Entrer un nom pour la nouvelle entrée dans le champ « Titre : ».

Sélectionner un dossier dans lequel la nouvelle entrée se trouvera dans le champ « Dossier : ».

Entrer un URL pour la nouvelle entrée dans le champ « URL : ».

Entrer le nom d'utilisateur dans le champ « Nom d'utilisateur : ».

Entrer le mot de passe dans le champ « Mot de passe : ». Vous pouvez cliquer sur le bouton pour obtenir un mot de passe aléatoire.

Entrer tout autre information à propos de l'entrée dans le champ « Notes : ».

Quand vous avez terminé d'entrer les informations, cliquer sur Valider

Il n'y a que le champ « Titre : » qui est requis. Tous les autres champs sont facultatifs et peuvent rester vides.

././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978179.0 pasaffe-0.57/help/fr/preferences.page0000644000175000017500000000435200000000000021227 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com Comment ajuster les réglages et préférences de Pasaffe. Réglage des préférences

Pour ajuster les préférences de Pasaffe, cliquer sur ÉditerPréférences. Une fenêtre apparaîtera avec des options. Quand les changements seront terminés, cliquer sur Fermer.

Voici quelques préférences qui peuvent être modifiées :

Enregistrer automatiquement les modifications : Lorsque coché, Pasaffe enregistrera automatiquement la base de données après chaque changement. Par défaut, les changements ne sont sauvegardés que lorsque l'utilisateur le demande, afin que les changements puissent être annulés.

Afficher les secrets par défaut : Lorsque coché, Pasaffe affichers toujours les secrets, tel que les mots de passe, dans la section de droite de la fenêtre principale.

Seuls les mot de passes sont secrets : Lorsque coché, il n'y a que le champ de mot de passe qui sera considéré comme étant un secret qui doit être caché par défaut. Décocher cette case si vous voulez que les autres données soient cachées par défaut aussi.

Verrouiller la base de données après (5) minutes : Lorsque coché, la fenêtre de Pasaffe verrouillera la base de données et exigera de nouveau le mot de passe principal après un certain nombre de minutes. Le nombre de minutes peut être ajusté.

Les mots de passe ont (12) caractères : Ceci permets d'ajuster la longueur des mots de passe aléatoires générés par le bouton.

Les dossiers : Lorsque Pasaffe démarre, les dossiers peuvent être ouverts, fermés, ou dans l'état du dernier sauvegarde.

././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1398978183.0 pasaffe-0.57/help/fr/show-secrets.page0000644000175000017500000000250600000000000021353 0ustar00mdeslaurmdeslaur00000000000000 Marc Deslauriers marc.deslauriers@canonical.com Comment afficher les secrets d'une entrée. Affichage des secrets

Les secrets, tels que les mots de passe, ne sont pas affichés par défaut dans la section de droite de la fenêtre de Pasaffe.

Afficher les secrets d'une entrée dans <app>Pasaffe</app>

Dans la section de gauche, sélectionner l'entrée que vous voulez afficher.

Cliquer sur l'icône qui ressemble à un trousseau de clé, ou sélectionner VisualiserAfficher les secrets.

Effecter l'opération de nouveau pour cacher les secrets affichés.

Vous pouvez paramétrer Pasaffe de sorte à afficher les secrets par défaut dans la fenêtre des préférences. Voir la page Réglage des préférences.

././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/mime/0000775000175000017500000000000000000000000015456 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1494620248.0 pasaffe-0.57/mime/pasaffe.xml0000644000175000017500000000044600000000000017607 0ustar00mdeslaurmdeslaur00000000000000 PasswordSafe v3 database ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/pasaffe/0000775000175000017500000000000000000000000016134 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613141.0 pasaffe-0.57/pasaffe/AboutPasaffeDialog.py0000644000175000017500000000241600000000000022167 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import logging from pasaffe_lib.AboutDialog import AboutDialog from pasaffe_lib import get_version logger = logging.getLogger('pasaffe') # pylint: disable=E1101 # See pasaffe_lib.AboutDialog.py for more details about how this class works. class AboutPasaffeDialog(AboutDialog): __gtype_name__ = "AboutPasaffeDialog" def finish_initializing(self, builder): # pylint: disable=E1002 """Set up the about dialog""" super(AboutPasaffeDialog, self).finish_initializing(builder) self.set_version(get_version()) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613291.0 pasaffe-0.57/pasaffe/EditDetailsDialog.py0000644000175000017500000000771300000000000022027 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gio, Gtk # noqa: E402 from gi.repository import Gdk # noqa: E402 from pasaffe_lib.helpersgui import get_builder # noqa: E402 from pasaffe_lib.helpers import gen_password # noqa: E402 # pylint: disable=E1101 class EditDetailsDialog(Gtk.Dialog): __gtype_name__ = "EditDetailsDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated EditDetailsDialog object. """ builder = get_builder('EditDetailsDialog') new_object = builder.get_object('edit_details_dialog') new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called when we're finished initializing. finish_initalizing should be called after parsing the ui definition and creating a EditDetailsDialog object with it in order to finish initializing the start of the new EditDetailsDialog instance. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self) settings = Gio.Settings.new("net.launchpad.pasaffe") self.password_length = settings.get_int("password-length") def on_btn_ok_clicked(self, widget, data=None): """The user has elected to save the changes. Called before the dialog returns Gtk.RESONSE_OK from run(). """ pass def on_btn_cancel_clicked(self, widget, data=None): """The user has elected cancel changes. Called before the dialog returns Gtk.ResponseType.CANCEL for run() """ pass def on_password_button_clicked(self, widget): """The user has clicked the password button""" self.show_passwords_menu(widget) def on_menuitem_activate(self, widget): """The user has clicked on a menu item""" label = widget.get_label() self.ui.password_entry.set_text(label) def show_passwords_menu(self, widget): """Generate some new passwords""" try: passwords = gen_password(6, self.password_length) self.ui.password1.set_label(passwords[0].decode('utf-8')) self.ui.password2.set_label(passwords[1].decode('utf-8')) self.ui.password3.set_label(passwords[2].decode('utf-8')) self.ui.password4.set_label(passwords[3].decode('utf-8')) self.ui.password5.set_label(passwords[4].decode('utf-8')) self.ui.password6.set_label(passwords[5].decode('utf-8')) except: # noqa: E722 pass if not Gtk.check_version(3, 22, 0): self.ui.password_menu.popup_at_widget(widget, Gdk.Gravity.SOUTH_WEST, Gdk.Gravity.NORTH_WEST, None) else: self.ui.password_menu.popup(None, None, None, None, 0, Gtk.get_current_event_time()) if __name__ == "__main__": dialog = EditDetailsDialog() dialog.show() Gtk.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613809.0 pasaffe-0.57/pasaffe/EditFolderDialog.py0000644000175000017500000000447700000000000021661 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 from pasaffe_lib.helpersgui import get_builder # noqa: E402 class EditFolderDialog(Gtk.Dialog): __gtype_name__ = "EditFolderDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated EditFolderDialog object. """ builder = get_builder('EditFolderDialog') new_object = builder.get_object('edit_folder_dialog') new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called when we're finished initializing. finish_initalizing should be called after parsing the ui definition and creating a EditFolderDialog object with it in order to finish initializing the start of the new EditFolderDialog instance. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self) def on_btn_ok_clicked(self, widget, data=None): """The user has elected to save the changes. Called before the dialog returns Gtk.ResponseType.OK from run(). """ pass def on_btn_cancel_clicked(self, widget, data=None): """The user has elected cancel changes. Called before the dialog returns Gtk.ResponseType.CANCEL for run() """ pass if __name__ == "__main__": dialog = EditFolderDialog() dialog.show() Gtk.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613830.0 pasaffe-0.57/pasaffe/NewDatabaseDialog.py0000644000175000017500000000450200000000000022003 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 from pasaffe_lib.helpersgui import get_builder # noqa: E402 class NewDatabaseDialog(Gtk.Dialog): __gtype_name__ = "NewDatabaseDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated NewDatabaseDialog object. """ builder = get_builder('NewDatabaseDialog') new_object = builder.get_object('new_database_dialog') new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called when we're finished initializing. finish_initalizing should be called after parsing the ui definition and creating a NewDatabaseDialog object with it in order to finish initializing the start of the new NewDatabaseDialog instance. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self) def on_btn_ok_clicked(self, widget, data=None): """The user has elected to save the changes. Called before the dialog returns Gtk.RESONSE_OK from run(). """ pass def on_btn_cancel_clicked(self, widget, data=None): """The user has elected cancel changes. Called before the dialog returns Gtk.ResponseType.CANCEL for run() """ pass if __name__ == "__main__": dialog = NewDatabaseDialog() dialog.show() Gtk.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613314.0 pasaffe-0.57/pasaffe/NewPasswordDialog.py0000644000175000017500000000450200000000000022101 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 from pasaffe_lib.helpersgui import get_builder # noqa: E402 class NewPasswordDialog(Gtk.Dialog): __gtype_name__ = "NewPasswordDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated NewPasswordDialog object. """ builder = get_builder('NewPasswordDialog') new_object = builder.get_object('new_password_dialog') new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called when we're finished initializing. finish_initalizing should be called after parsing the ui definition and creating a NewPasswordDialog object with it in order to finish initializing the start of the new NewPasswordDialog instance. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self) def on_btn_ok_clicked(self, widget, data=None): """The user has elected to save the changes. Called before the dialog returns Gtk.RESONSE_OK from run(). """ pass def on_btn_cancel_clicked(self, widget, data=None): """The user has elected cancel changes. Called before the dialog returns Gtk.ResponseType.CANCEL for run() """ pass if __name__ == "__main__": dialog = NewPasswordDialog() dialog.show() Gtk.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613730.0 pasaffe-0.57/pasaffe/PasaffeWindow.py0000644000175000017500000021335500000000000021252 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') import gi # noqa: E402 gi.require_version('Gtk', '3.0') from gi.repository import Gio, Gtk # noqa: E402 from gi.repository import Gdk, Pango, GLib # noqa: E402 import logging # noqa: E402 import os # noqa: E402 import webbrowser # noqa: E402 from pasaffe_lib.Window import Window # noqa: E402 from pasaffe.AboutPasaffeDialog import AboutPasaffeDialog # noqa: E402 from pasaffe.EditDetailsDialog import EditDetailsDialog # noqa: E402 from pasaffe.EditFolderDialog import EditFolderDialog # noqa: E402 from pasaffe.PasswordEntryDialog import PasswordEntryDialog # noqa: E402 from pasaffe.SaveChangesDialog import SaveChangesDialog # noqa: E402 from pasaffe.NewDatabaseDialog import NewDatabaseDialog # noqa: E402 from pasaffe.NewPasswordDialog import NewPasswordDialog # noqa: E402 from pasaffe.PreferencesPasaffeDialog import \ PreferencesPasaffeDialog # noqa: E402 from pasaffe_lib.readdb import PassSafeFile # noqa: E402 from pasaffe_lib.helpersgui import get_builder # noqa: E402 from pasaffe_lib.helpers import folder_list_to_field # noqa: E402 from pasaffe_lib.helpers import field_to_folder_list # noqa: E402 from pasaffe_lib.helpers import folder_list_to_path # noqa: E402 from pasaffe_lib.helpers import folder_path_to_list # noqa: E402 from pasaffe_lib.helpers import PathEntry # noqa: E402 logger = logging.getLogger('pasaffe') # See pasaffe_lib.Window.py for more details about how this class works class PasaffeWindow(Window): __gtype_name__ = "PasaffeWindow" def __new__(cls, database=None): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated BasePasaffeWindow object. """ builder = get_builder('PasaffeWindow') new_object = builder.get_object("pasaffe_window") new_object.finish_initializing(builder, database) return new_object def finish_initializing(self, builder, database): # pylint: disable=E1002 """Set up the main window""" super(PasaffeWindow, self).finish_initializing(builder) self.AboutDialog = AboutPasaffeDialog self.EditDetailsDialog = EditDetailsDialog self.editdetails_dialog = None self.EditFolderDialog = EditFolderDialog self.editfolder_dialog = None self.PreferencesDialog = PreferencesPasaffeDialog self.PasswordEntryDialog = PasswordEntryDialog self.SaveChangesDialog = SaveChangesDialog self.NewDatabaseDialog = NewDatabaseDialog self.NewPasswordDialog = NewPasswordDialog self.connect("delete-event", self.on_delete_event) self.ui.textview1.connect("motion-notify-event", self.textview_event_handler) self.passfile = None self.is_locked = False self.idle_id = None self.clipboard_id = None self.last_copied = None self.folder_state = {} if database is None: self.database = self.settings.get_string('database-path') else: self.database = database self.default_database = database is None self.set_save_status(False) self.settings = Gio.Settings.new("net.launchpad.pasaffe") self.settings.connect('changed', self.on_preferences_changed) self.state = Gio.Settings.new("net.launchpad.pasaffe.state") # If database doesn't exists, make a new one if os.path.exists(self.database): success = self.fetch_password() else: success = self.new_database() if success is False: self.connect('event-after', Gtk.main_quit) else: self.set_window_size() self.set_show_password_status() self.display_entries() self.set_initial_tree_expansion() self.display_welcome() self.ui.treeview1.grab_focus() # Set inactivity timer self.set_idle_timeout() def on_delete_event(self, _widget, _event): self.save_window_size() return self.save_warning() def set_window_size(self): width = self.state.get_int('main-size-width') height = self.state.get_int('main-size-height') split = self.state.get_int('main-split') self.ui.pasaffe_window.resize(width, height) self.ui.hpaned1.set_position(split) def save_window_size(self): (width, height) = self.ui.pasaffe_window.get_size() split = self.ui.hpaned1.get_position() self.state.set_int('main-size-width', width) self.state.set_int('main-size-height', height) self.state.set_int('main-split', split) def set_entry_window_size(self): width = self.state.get_int('entry-size-width') height = self.state.get_int('entry-size-height') self.editdetails_dialog.ui.edit_details_dialog.resize(width, height) def save_entry_window_size(self): (width, height) = \ self.editdetails_dialog.ui.edit_details_dialog.get_size() self.state.set_int('entry-size-width', width) self.state.set_int('entry-size-height', height) def set_folder_window_size(self): width = self.state.get_int('folder-size-width') height = self.state.get_int('folder-size-height') self.editfolder_dialog.ui.edit_folder_dialog.resize(width, height) def save_folder_window_size(self): (width, height) = \ self.editfolder_dialog.ui.edit_folder_dialog.get_size() self.state.set_int('folder-size-width', width) self.state.set_int('folder-size-height', height) def save_warning(self): if self.get_save_status() is True: savechanges_dialog = self.SaveChangesDialog() savechanges_dialog.set_transient_for(self) response = savechanges_dialog.run() if response == Gtk.ResponseType.OK: self.save_db() elif response != Gtk.ResponseType.CLOSE: savechanges_dialog.destroy() return True return False def fetch_password(self): success = True password_dialog = self.PasswordEntryDialog() password_dialog.set_transient_for(self) while self.passfile is None: response = password_dialog.run() if response == Gtk.ResponseType.OK: password = password_dialog.ui.password_entry.get_text() try: self.passfile = PassSafeFile(self.database, password) except ValueError: password_dialog.ui.password_error_label.set_property( "visible", True) password_dialog.ui.password_entry.set_text("") password_dialog.ui.password_entry.grab_focus() else: success = False break password_dialog.destroy() return success def new_database(self): success = False newdb_dialog = self.NewDatabaseDialog() newdb_dialog.set_transient_for(self) while success is False: response = newdb_dialog.run() if response == Gtk.ResponseType.OK: password_a = newdb_dialog.ui.entry1.get_text() password_b = newdb_dialog.ui.entry2.get_text() if password_a != password_b: newdb_dialog.ui.error_label.set_property("visible", True) newdb_dialog.ui.entry1.grab_focus() else: self.passfile = PassSafeFile() self.passfile.new_db(password_a) success = True else: break newdb_dialog.destroy() return success def create_folders(self, folders): parent = None if not folders: return None node = self.ui.liststore1.get_iter_first() for folder in folders: if not folder: return parent found = False while node is not None and not found: if (self.ui.liststore1.get_value(node, 1) == folder) and \ ("pasaffe_treenode." in self.ui.liststore1.get_value( node, 2)): found = True parent = node if self.ui.liststore1.iter_has_child(node): node = self.ui.liststore1.iter_children(node) else: break if not found: node = self.ui.liststore1.iter_next(node) if not found: parent = self.ui.liststore1.append(parent, ["inode-directory-symbolic", folder, "pasaffe_treenode." + folder]) node = self.ui.liststore1.iter_children(parent) return parent def _fixup_folders(self, folder_list): if folder_list: return [_("[Untitled]") if x == "" else x for x in folder_list] return None def display_entries(self): entries = [] # Add empty folders first for folder in self.passfile.get_empty_folders(): folder = self._fixup_folders(folder) entry = PathEntry("", "", folder) entries.append(entry) # Then add records for uuid in self.passfile.records: title = self.passfile.get_title(uuid) username = self.passfile.get_username(uuid) folder = self._fixup_folders(self.passfile.get_folder_list(uuid)) # Empty names don't display properly in tree if title in [None, ""]: title = _("[Untitled]") elif (username not in [None, ""] and self.settings.get_boolean('display-usernames') is True): title = title + " [" + username + "]" entry = PathEntry(title, uuid, folder) entries.append(entry) self.ui.liststore1.clear() # Then sort and add for record in sorted(entries): parent = self.create_folders(record.path) if record.name != "": self.ui.liststore1.append(parent, ["text-x-generic-symbolic", record.name, record.uuid]) self.set_tree_expansion() self.set_menu_for_entry(False) # enable drag and drop dnd_targets = [('MY_TREE_MODEL_ROW', Gtk.TargetFlags.SAME_WIDGET, 0)] self.ui.treeview1.enable_model_drag_source( Gdk.ModifierType.BUTTON1_MASK, dnd_targets, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) self.ui.treeview1.enable_model_drag_dest(dnd_targets, Gdk.DragAction.DEFAULT) self.ui.treeview1.connect("drag_data_received", self.drag_data_received_data) def set_tree_expansion(self): folders = list(self.folder_state.copy().keys()) folders.sort() for folder in folders: folder_iter = self.search_folder(field_to_folder_list(folder)) if folder_iter is not None: path = self.ui.treeview1.get_model().get_path(folder_iter) if self.folder_state[folder] is True: self.ui.treeview1.expand_row(path, False) else: self.ui.treeview1.collapse_row(path) def set_folder_state(self, folder, state): folder_field = folder_list_to_field(folder) self.folder_state[folder_field] = state def set_initial_tree_expansion(self): entries = [] expansion_status = self.passfile.get_tree_status() config = self.settings.get_string('tree-expansion') if config == "collapsed": self.ui.treeview1.collapse_all() return elif config == "expanded" or expansion_status is None: self.ui.treeview1.expand_all() return for folder in self.passfile.get_all_folders(): entry = PathEntry("", "", folder) entries.append(entry) index = 0 for record in sorted(entries): if index + 1 > len(expansion_status): return folder_iter = self.search_folder(record.path) if folder_iter is not None: path = self.ui.treeview1.get_model().get_path(folder_iter) if expansion_status[index] == "1": # FIXME: For some reason, GtkTreeView will not expand # a folder inside a folder that is collapsed. Need to # find a workaround. self.ui.treeview1.expand_row(path, False) index += 1 def save_tree_expansion(self): entries = [] expansion_status = "" for folder in self.passfile.get_all_folders(): entry = PathEntry("", "", folder) entries.append(entry) for record in sorted(entries): if self.folder_state.get(folder_list_to_field(record.path), False) is True: expansion_status += "1" else: expansion_status += "0" if expansion_status == "": expansion_status = None self.passfile.set_tree_status(expansion_status) def drag_data_received_data(self, treeview, _context, x, y, _selection, _info, _etime): sourcemodel, sourceiter = treeview.get_selection().get_selected() source_uuid = sourcemodel.get_value(sourceiter, 2) destmodel = treeview.get_model() drop_info = treeview.get_dest_row_at_pos(x, y) if drop_info: path, position = drop_info destiter = destmodel.get_iter(path) dest_uuid = destmodel.get_value(destiter, 2) # Ignore entries as drop destinations if "pasaffe_treenode." not in dest_uuid: return if ((position == Gtk.TreeViewDropPosition.INTO_OR_BEFORE) or (position == Gtk.TreeViewDropPosition.INTO_OR_AFTER)): new_parents = self.get_folders_from_iter(destmodel, destiter) if "pasaffe_treenode." in source_uuid: current_folders = self.get_folders_from_iter(sourcemodel, sourceiter) parent_folders = current_folders[:-1] # Bail out if the folder is dragged onto itself if current_folders == new_parents: return # Bail out if we're trying to drag a folder into a # subdirectory of itself if current_folders == new_parents[:len(current_folders)]: return if new_parents != parent_folders: new_folders = new_parents[:] new_folders.append(current_folders[-1:][0]) old_folders = current_folders[:] self.passfile.rename_folder_list(old_folders, new_folders) self.display_entries() self.goto_folder(new_folders) self.set_save_status(True) if self.settings.get_boolean('auto-save') is True: self.save_db() else: parent_folders = self.passfile.get_folder_list(source_uuid) if new_parents != parent_folders: self.passfile.remove_empty_folder(new_parents) self.passfile.add_empty_folder(parent_folders) self.passfile.update_folder_list(source_uuid, new_parents) self.passfile.update_modification_time(source_uuid) self.display_entries() self.goto_uuid(source_uuid) self.set_save_status(True) if self.settings.get_boolean('auto-save') is True: self.save_db() self.set_idle_timeout() self.update_find_results(force=True) def goto_uuid(self, uuid): item = self.search_uuid(uuid) if item is not None: treemodel = self.ui.treeview1.get_model() # See if we need to expand some folders parent = treemodel.iter_parent(item) while parent is not None: path = treemodel.get_path(parent) self.ui.treeview1.expand_row(path, False) parent = treemodel.iter_parent(parent) if "pasaffe_treenode." in uuid: self.display_folder(treemodel.get_value(item, 1)) else: self.display_data(uuid) path = treemodel.get_path(item) self.ui.treeview1.scroll_to_cell(path) self.ui.treeview1.set_cursor(path) def goto_folder(self, folders): item = self.search_folder(folders) if item is not None: self.display_folder(self.ui.liststore1.get_value(item, 1)) path = self.ui.treeview1.get_model().get_path(item) self.ui.treeview1.scroll_to_cell(path) self.ui.treeview1.set_cursor(path) def search_uuid(self, uuid, item=None, toplevel=True): if toplevel is True: item = self.ui.treeview1.get_model().get_iter_first() while item: if self.ui.liststore1.get_value(item, 2) == uuid: return item result = self.search_uuid( uuid, self.ui.treeview1.get_model().iter_children(item), False) if result: return result item = self.ui.treeview1.get_model().iter_next(item) return None def search_folder(self, folders): parent = None if not folders: return None node = self.ui.liststore1.get_iter_first() for folder in folders: if not folder: return parent found = False while node is not None and not found: if (self.ui.liststore1.get_value(node, 1) == folder) and \ ("pasaffe_treenode." in self.ui.liststore1.get_value( node, 2)): found = True parent = node if self.ui.liststore1.iter_has_child(node): node = self.ui.liststore1.iter_children(node) else: break if not found: node = self.ui.liststore1.iter_next(node) if not found: return None return parent def find_prev_iter(self, uuid, item=None, toplevel=True, prev_item=None): if toplevel is True: item = self.ui.treeview1.get_model().get_iter_first() if item is None: return prev_item, None while item: if self.ui.liststore1.get_value(item, 2) == uuid: return prev_item, item prev_item, result = self.find_prev_iter( uuid, self.ui.treeview1.get_model().iter_children(item), False, item) if result: return prev_item, result prev_item = item item = self.ui.treeview1.get_model().iter_next(item) return item, None def display_data(self, entry_uuid, show_secrets=False): ttt = self.get_texttagtable() data_buffer = Gtk.TextBuffer.new(ttt) # title data_buffer.insert(data_buffer.get_start_iter(), "\n") title = self.passfile.get_title(entry_uuid) # Fixup Empty names if title in [None, ""]: title = _("[Untitled]") data_buffer.insert_with_tags(data_buffer.get_end_iter(), title, ttt.lookup('title')) data_buffer.insert(data_buffer.get_end_iter(), "\n\n") # url if self.passfile.get_url(entry_uuid): data_buffer.insert_with_tags(data_buffer.get_end_iter(), _("URL:") + "\n", ttt.lookup('section')) data_buffer.insert_with_tags( data_buffer.get_end_iter(), self.passfile.get_url(entry_uuid), ttt.lookup('url')) data_buffer.insert(data_buffer.get_end_iter(), "\n\n") if show_secrets is False and \ self.settings.get_boolean('only-passwords-are-secret') is False \ and self.settings.get_boolean('visible-secrets') is False: data_buffer.insert(data_buffer.get_end_iter(), _("Secrets are currently hidden.")) else: # username data_buffer.insert_with_tags(data_buffer.get_end_iter(), _("Username:") + "\n", ttt.lookup('section')) data_buffer.insert(data_buffer.get_end_iter(), self.passfile.get_username(entry_uuid)) data_buffer.insert(data_buffer.get_end_iter(), "\n\n") # password data_buffer.insert_with_tags(data_buffer.get_end_iter(), _("Password:") + "\n", ttt.lookup('section')) if show_secrets is True or \ self.settings.get_boolean('visible-secrets') is True: data_buffer.insert_with_tags(data_buffer.get_end_iter(), self.passfile.get_password( entry_uuid), ttt.lookup('password')) else: data_buffer.insert(data_buffer.get_end_iter(), '*****') data_buffer.insert(data_buffer.get_end_iter(), "\n\n") # notes if self.passfile.get_notes(entry_uuid): data_buffer.insert_with_tags(data_buffer.get_end_iter(), _("Notes:") + "\n", ttt.lookup('section')) data_buffer.insert(data_buffer.get_end_iter(), self.passfile.get_notes(entry_uuid)) data_buffer.insert(data_buffer.get_end_iter(), "\n\n") # modification time last_updated = self.passfile.get_modification_time(entry_uuid) if last_updated is not None: data_buffer.insert_with_tags(data_buffer.get_end_iter(), _("Last updated:") + "\n", ttt.lookup('section')) data_buffer.insert(data_buffer.get_end_iter(), last_updated) data_buffer.insert(data_buffer.get_end_iter(), "\n") # password updated time pass_updated = self.passfile.get_password_time(entry_uuid) if pass_updated is not None: data_buffer.insert_with_tags(data_buffer.get_end_iter(), _("Password updated:") + "\n", ttt.lookup('section')) data_buffer.insert(data_buffer.get_end_iter(), pass_updated) data_buffer.insert(data_buffer.get_end_iter(), "\n\n") self.ui.textview1.set_buffer(data_buffer) self.set_menu_for_entry(True) # Now disable menus for blank entries if self.passfile.get_url(entry_uuid) in [None, ""]: self.ui.url_copy.set_sensitive(False) self.ui.mnu_open_url.set_sensitive(False) self.ui.url_copy1.set_sensitive(False) self.ui.open_url.set_sensitive(False) if self.passfile.get_username(entry_uuid) in [None, ""]: self.ui.username_copy.set_sensitive(False) self.ui.username_copy1.set_sensitive(False) self.ui.copy_username.set_sensitive(False) if self.passfile.get_password(entry_uuid) in [None, ""]: self.ui.password_copy.set_sensitive(False) self.ui.password_copy1.set_sensitive(False) self.ui.copy_password.set_sensitive(False) def display_welcome(self): ttt = self.get_texttagtable() data_buffer = Gtk.TextBuffer.new(ttt) data_buffer.insert(data_buffer.get_start_iter(), "\n") data_buffer.insert_with_tags(data_buffer.get_end_iter(), _("Welcome to Pasaffe!"), ttt.lookup('title')) data_buffer.insert(data_buffer.get_end_iter(), "\n\n") data_buffer.insert(data_buffer.get_end_iter(), _("Pasaffe is an easy to use\npassword manager.")) self.ui.textview1.set_buffer(data_buffer) def display_folder(self, folder_name): ttt = self.get_texttagtable() data_buffer = Gtk.TextBuffer.new(ttt) data_buffer.insert(data_buffer.get_start_iter(), "\n") data_buffer.insert_with_tags(data_buffer.get_end_iter(), folder_name, ttt.lookup('title')) data_buffer.insert(data_buffer.get_end_iter(), "\n\n") data_buffer.insert(data_buffer.get_end_iter(), _("This is a folder.")) self.ui.textview1.set_buffer(data_buffer) self.set_menu_for_entry(False) def get_texttagtable(self): texttagtable = Gtk.TextTagTable() texttag_big = Gtk.TextTag.new("title") texttag_big.set_property("weight", Pango.Weight.BOLD) texttag_big.set_property("size", 14 * Pango.SCALE) texttagtable.add(texttag_big) texttag_section = Gtk.TextTag.new("section") texttag_section.set_property("weight", Pango.Weight.BOLD) texttagtable.add(texttag_section) texttag_url = Gtk.TextTag.new("url") texttag_url.set_property("foreground", "blue") texttag_url.set_property("underline", Pango.Underline.SINGLE) texttag_url.connect("event", self.url_event_handler) texttagtable.add(texttag_url) texttag_password = Gtk.TextTag.new("password") texttag_password.set_property("font", 'Mono') texttagtable.add(texttag_password) return texttagtable def url_event_handler(self, _tag, _widget, event, _iter): # We also used to check event.button == 1 here, but event.button # doesn't seem to get set by PyGObject anymore. if event.type == Gdk.EventType.BUTTON_RELEASE: self.open_url() return False def textview_event_handler(self, textview, event): loc_x, loc_y = textview.window_to_buffer_coords( Gtk.TextWindowType.WIDGET, int(event.x), int(event.y)) # Fix dumb Gtk 3.20 API change try: itera = textview.get_iter_at_location(loc_x, loc_y) tags = itera.get_tags() except AttributeError: (_over, itera) = textview.get_iter_at_location(loc_x, loc_y) tags = itera.get_tags() cursor = Gdk.Cursor.new(Gdk.CursorType.XTERM) for tag in tags: if tag.get_property('name') == 'url': cursor = Gdk.Cursor.new(Gdk.CursorType.HAND2) break textview.get_window(Gtk.TextWindowType.TEXT).set_cursor(cursor) return False def open_url(self): url = None treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) # Bail out of we're a folder if "pasaffe_treenode." in entry_uuid: return url = self.passfile.get_url(entry_uuid) if url is not None: if not url.startswith('http://') and \ not url.startswith('https://'): url = 'http://' + url webbrowser.open(url) def on_treeview1_cursor_changed(self, treeview): self.set_idle_timeout() selection = treeview.get_selection() if selection is not None: treemodel, treeiter = selection.get_selected() if treemodel is not None and treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: self.display_folder(treemodel.get_value(treeiter, 1)) else: self.display_data(entry_uuid) # Reset the show password button and menu item self.ui.display_secrets.set_active(False) self.ui.mnu_display_secrets.set_active(False) def on_treeview1_button_press_event(self, treeview, event): if event.button == 3: loc_x = int(event.x) loc_y = int(event.y) event_time = event.time pthinfo = treeview.get_path_at_pos(loc_x, loc_y) if pthinfo is not None: path, col, _cellx, _celly = pthinfo treeview.grab_focus() treeview.set_cursor(path, col, 0) if not Gtk.check_version(3, 22, 0): self.ui.menu_popup.popup_at_pointer(event) else: self.ui.menu_popup.popup(None, None, None, None, 3, event_time) def add_entry(self): self.disable_idle_timeout() # Make sure dialog isn't already open if self.editdetails_dialog is not None: self.editdetails_dialog.present() return uuid_hex = self.passfile.new_entry() treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() folder = None if treeiter is not None: folder = self.get_folders_from_iter(treemodel, treeiter) if folder is not None: # expand folder path = self.ui.treeview1.get_model().get_path(treeiter) self.ui.treeview1.expand_row(path, False) self.passfile.update_folder_list(uuid_hex, folder) response = self.edit_entry(uuid_hex, True) if response != Gtk.ResponseType.OK: self.delete_entry(uuid_hex, save=False) else: self.display_entries() self.goto_uuid(uuid_hex) self.set_save_status(True) if self.settings.get_boolean('auto-save') is True: self.save_db() self.set_idle_timeout() self.update_find_results(force=True) def add_folder(self): self.disable_idle_timeout() # Make sure dialog isn't already open if self.editfolder_dialog is not None: self.editfolder_dialog.present() return # Get currently selected folder treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." not in entry_uuid: treeiter = None # TODO: make sure folder name is unique in same level new_iter = self.ui.liststore1.append(treeiter, ['inode-directory-symbolic', _('New Folder'), "pasaffe_treenode.New Folder"]) if treeiter is not None: path = self.ui.treeview1.get_model().get_path(treeiter) self.ui.treeview1.expand_row(path, False) self.ui.treeview1.get_selection().select_iter(new_iter) self.display_folder(self.ui.liststore1.get_value(new_iter, 1)) new_folder = self.get_folders_from_iter(treemodel, new_iter) response = self.edit_folder(self.ui.liststore1, new_iter, True) if response != Gtk.ResponseType.OK: self.delete_folder(new_folder, save=False) def clone_entry(self, entry_uuid): record_list = (2, 3, 4, 5, 6, 13) self.disable_idle_timeout() # Make sure dialog isn't already open if self.editdetails_dialog is not None: self.editdetails_dialog.present() return uuid_hex = self.passfile.new_entry() for record_type in record_list: if record_type in self.passfile.records[entry_uuid]: self.passfile.records[uuid_hex][record_type] = \ self.passfile.records[entry_uuid][record_type] response = self.edit_entry(uuid_hex) if response != Gtk.ResponseType.OK: self.delete_entry(uuid_hex, save=False) else: self.display_entries() self.goto_uuid(uuid_hex) self.set_save_status(True) if self.settings.get_boolean('auto-save') is True: self.save_db() self.set_idle_timeout() self.update_find_results(force=True) def remove_entry(self): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) entry_name = treemodel.get_value(treeiter, 1) information = \ _('Are you sure you wish to' ' remove "%s"?\n\n') % \ GLib.markup_escape_text(entry_name) information += _('Contents of the entry will be lost.\n') info_dialog = Gtk.MessageDialog( transient_for=self, modal=True, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.YES_NO) info_dialog.set_markup(information) result = info_dialog.run() info_dialog.destroy() if result == Gtk.ResponseType.YES: self.delete_entry(entry_uuid) def remove_folder(self): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: folder_name = treemodel.get_value(treeiter, 1) information = \ _('Are you sure you wish' ' to remove folder "%s"?\n\n') % \ GLib.markup_escape_text(folder_name) information += _('All entries in this folder will be lost.\n') info_dialog = Gtk.MessageDialog( transient_for=self, modal=True, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.YES_NO) info_dialog.set_markup(information) result = info_dialog.run() info_dialog.destroy() if result == Gtk.ResponseType.YES: folder = self.get_folders_from_iter(treemodel, treeiter) self.delete_folder(folder) def populate_folders(self, liststore, combobox, default=None): folders = [["/"]] for folder in self.passfile.get_all_folders(): for index in range(len(folder)): folder_path = [folder_list_to_path(folder, index)] if folder_path not in folders: folders.append(folder_path) folders.sort() for folder in folders: liststore.append(folder) if default is not None: item = self.search_folder_ui(liststore, folder_list_to_path(default)) if item is not None: combobox.set_active_iter(item) def search_folder_ui(self, liststore, folder): item = liststore.get_iter_first() while item: if liststore.get_value(item, 0) == folder: return item item = liststore.iter_next(item) return None def edit_entry(self, entry_uuid, new_entry=False): if "pasaffe_treenode." in entry_uuid: return None record_dict = {2: 'folder_entry', 3: 'name_entry', 4: 'username_entry', 5: 'notes_buffer', 6: 'password_entry', 13: 'url_entry'} # Make sure dialog isn't already open if self.editdetails_dialog is not None: self.editdetails_dialog.present() return if self.EditDetailsDialog is not None: self.disable_idle_timeout() self.editdetails_dialog = self.EditDetailsDialog() if new_entry is True: title = self.editdetails_dialog.builder.get_object('title') title.set_markup(_("New entry")) for record_type, widget_name in list(record_dict.items()): # Handle folders separately if record_type == 2: liststore = \ self.editdetails_dialog.builder.get_object( 'liststore1') combobox = \ self.editdetails_dialog.builder.get_object( 'folder_combo') if self.passfile.get_folder_list(entry_uuid): self.populate_folders( liststore, combobox, self.passfile.get_folder_list(entry_uuid)) else: self.populate_folders(liststore, combobox, []) elif record_type == 3: self.editdetails_dialog.builder.get_object( widget_name).set_text( self.passfile.get_title(entry_uuid)) elif record_type in self.passfile.records[entry_uuid]: self.editdetails_dialog.builder.get_object( widget_name).set_text( self.passfile.records[entry_uuid][record_type]) # Set a Mono font so certain characters don't look alike # How do I do this in the glade file? password_entry = self.editdetails_dialog.builder.get_object( 'password_entry') password_entry.modify_font(Pango.FontDescription('Mono')) self.set_entry_window_size() self.editdetails_dialog.set_transient_for(self) response = self.editdetails_dialog.run() data_changed = False tree_changed = False if response == Gtk.ResponseType.OK: for record_type, widget_name in list(record_dict.items()): # Get the new value if record_type == 2: combo = self.editdetails_dialog.builder.get_object( 'folder_combo') combo_iter = combo.get_active_iter() if combo_iter is not None: new_value = folder_path_to_list( combo.get_model()[combo_iter][0]) else: new_value = folder_path_to_list( combo.get_child().get_text()) elif record_type == 5: new_value = self.editdetails_dialog.builder.get_object( widget_name).get_text( self.editdetails_dialog.builder.get_object( widget_name).get_start_iter(), self.editdetails_dialog.builder.get_object( widget_name).get_end_iter(), True) else: new_value = self.editdetails_dialog.builder.get_object( widget_name).get_text() # Now do something with it if record_type == 2: old_folder = self.passfile.get_folder_list(entry_uuid) self.passfile.remove_empty_folder(new_value) if old_folder != new_value: self.passfile.add_empty_folder(old_folder) self.passfile.update_folder_list( entry_uuid, new_value) data_changed = True tree_changed = True elif ((record_type in [5, 13]) and new_value == "" and record_type in self.passfile.records[entry_uuid]): del self.passfile.records[entry_uuid][record_type] data_changed = True elif self.passfile.records[entry_uuid].get( record_type, "") != new_value: data_changed = True self.passfile.records[entry_uuid][record_type] = \ new_value # Reset the entire tree on name and path changes if record_type in [2, 3, 4]: tree_changed = True # Update the password changed date if record_type == 6: self.passfile.update_password_time(entry_uuid) if data_changed is True: self.set_save_status(True) self.passfile.update_modification_time(entry_uuid) if self.settings.get_boolean('auto-save') is True: self.save_db() self.save_entry_window_size() self.editdetails_dialog.destroy() self.editdetails_dialog = None if tree_changed is True: self.display_entries() self.goto_uuid(entry_uuid) else: # Update the right pane only if it's still the one # currently selected treemodel, treeiter = \ self.ui.treeview1.get_selection().get_selected() if treeiter is not None and treemodel.get_value( treeiter, 2) == entry_uuid: self.display_data(entry_uuid) self.set_idle_timeout() self.update_find_results(force=True) return response def edit_folder(self, treemodel, treeiter, new_folder=False): # Make sure dialog isn't already open if self.editfolder_dialog is not None: self.editfolder_dialog.present() return if self.EditFolderDialog is not None: self.disable_idle_timeout() self.editfolder_dialog = self.EditFolderDialog() if new_folder is True: title = self.editfolder_dialog.builder.get_object('title') title.set_markup(_("New folder")) folder_name = treemodel.get_value(treeiter, 1) if folder_name == _("[Untitled]"): folder_name = "" self.editfolder_dialog.ui.folder_name_entry.set_text(folder_name) self.editfolder_dialog.ui.folder_name_entry.select_region(0, -1) liststore = self.editfolder_dialog.builder.get_object('liststore1') combobox = self.editfolder_dialog.builder.get_object( 'folder_combo') parent_folder = self.get_folders_from_iter( treemodel, treeiter)[:-1] self.populate_folders(liststore, combobox, parent_folder) self.set_folder_window_size() self.editfolder_dialog.set_transient_for(self) response = self.editfolder_dialog.run() if response == Gtk.ResponseType.OK: new_name = \ self.editfolder_dialog.ui.folder_name_entry.get_text() combo_iter = combobox.get_active_iter() if combo_iter is not None: new_parent = folder_path_to_list( combobox.get_model()[combo_iter][0]) else: new_parent = [] if new_name != folder_name or new_parent != parent_folder: # TODO: make sure new_name is unique in the same level new_folders = new_parent[:] new_folders.append(new_name) old_folders = parent_folder[:] old_folders.append(folder_name) if new_folder is False: self.passfile.rename_folder_list( old_folders, new_folders) else: self.passfile.add_empty_folder(new_folders) self.display_entries() self.goto_folder(new_folders) self.set_save_status(True) if self.settings.get_boolean('auto-save') is True: self.save_db() self.save_folder_window_size() self.editfolder_dialog.destroy() self.editfolder_dialog = None self.set_idle_timeout() self.update_find_results(force=True) return response def get_folders_from_iter(self, treemodel, treeiter): folders = [] if treemodel is None or treeiter is None: return folders uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in uuid: folders.append(treemodel.get_value(treeiter, 1)) parent = treemodel.iter_parent(treeiter) while parent is not None: folders.insert(0, treemodel.get_value(parent, 1)) parent = treemodel.iter_parent(parent) return folders def delete_entry(self, entry_uuid, save=True): self.set_idle_timeout() item = self.search_uuid(entry_uuid) if item: new_item = self.ui.treeview1.get_model().iter_next(item) if new_item is None: # No more items in the current level, try and get the parent new_item, _item = self.find_prev_iter(entry_uuid) self.ui.liststore1.remove(item) # Delete the entry, and add the folder to the empty list folder = self.passfile.get_folder_list(entry_uuid) self.passfile.delete_entry(entry_uuid) self.passfile.add_empty_folder(folder) if item: if new_item is None: new_item = self.ui.treeview1.get_model().get_iter_first() if new_item != 0 and new_item is not None: self.ui.treeview1.get_selection().select_iter(new_item) if save is True: self.set_save_status(True) if self.settings.get_boolean('auto-save') is True: self.save_db() treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: self.display_folder(treemodel.get_value(treeiter, 1)) else: self.display_data(entry_uuid) else: self.display_welcome() self.update_find_results(force=True) def delete_folder(self, folders, save=True): self.set_idle_timeout() item = self.search_folder(folders) if item: self.passfile.delete_folder(folders) self.display_entries() parent_folder = folders[:-1] # TODO: if top level, switch to next iter if parent_folder != []: self.goto_folder(parent_folder) if save is True: self.set_save_status(True) if self.settings.get_boolean('auto-save') is True: self.save_db() treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: self.display_folder(treemodel.get_value(treeiter, 1)) else: self.display_data(entry_uuid) else: self.display_welcome() self.update_find_results(force=True) def model_get_iter_last(self, model, parent=None): """Returns a Gtk.TreeIter to the last row or None if there aren't any rows. If parent is None, returns a Gtk.TreeIter to the last root row.""" nchild = model.iter_n_children(parent) return nchild and model.iter_nth_child(parent, nchild - 1) def on_treeview1_row_activated(self, treeview, _path, _view_column): treemodel, treeiter = treeview.get_selection().get_selected() entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: # Toggle expanded state of folder folder = self.get_folders_from_iter(treemodel, treeiter) folder_field = folder_list_to_field(folder) if folder_field in self.folder_state and \ self.folder_state[folder_field] is True: self.collapse_folder(folder) else: self.expand_folder(folder) else: if self.settings.get_string('double-click') == "copies": # Copy password self.copy_selected_entry_item(6) else: self.edit_entry(entry_uuid) def collapse_folder(self, folder): self.set_folder_state(folder, False) self.set_tree_expansion() def expand_folder(self, folder): self.set_folder_state(folder, True) self.set_tree_expansion() def on_treeview1_row_expanded(self, treeview, treeiter, _path): treemodel = treeview.get_model() folder = self.get_folders_from_iter(treemodel, treeiter) self.set_folder_state(folder, True) def on_treeview1_row_collapsed(self, treeview, treeiter, _path): treemodel = treeview.get_model() folder = self.get_folders_from_iter(treemodel, treeiter) self.set_folder_state(folder, False) def on_treeview1_key_pressed(self, treeview, event): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is None: return entry_uuid = treemodel.get_value(treeiter, 2) if event.keyval in [Gdk.KEY_Return, Gdk.KEY_KP_Enter]: if "pasaffe_treenode." in entry_uuid: return else: self.edit_entry(entry_uuid) return True if event.keyval == Gdk.KEY_Left: if "pasaffe_treenode." in entry_uuid: folder = self.get_folders_from_iter(treemodel, treeiter) folder_field = folder_list_to_field(folder) if folder_field in self.folder_state and \ self.folder_state[folder_field] is True: self.collapse_folder(folder) return True folders = self.get_folders_from_iter(treemodel, treeiter)[:-1] else: folders = self.get_folders_from_iter(treemodel, treeiter) if folders: # Select parent folder self.goto_folder(folders) return True if event.keyval == Gdk.KEY_Right: if "pasaffe_treenode." in entry_uuid: folder = self.get_folders_from_iter(treemodel, treeiter) folder_field = folder_list_to_field(folder) if folder_field not in self.folder_state or \ self.folder_state[folder_field] is False: self.expand_folder(folder) return True # Select first child item iterchild = \ self.ui.treeview1.get_model().iter_children(treeiter) if iterchild: uuid = treemodel.get_value(iterchild, 2) self.goto_uuid(uuid) return True def save_db(self): if self.get_save_status() is True: self.save_tree_expansion() self.passfile.writefile(self.database, backup=True) self.set_save_status(False) def on_save_clicked(self, _toolbutton): self.set_idle_timeout() self.save_db() def on_mnu_save_activate(self, _menuitem): self.set_idle_timeout() self.save_db() def on_mnu_close_activate(self, _menuitem): self.disable_idle_timeout() if self.settings.get_boolean('auto-save') is True: self.save_db() if self.save_warning() is False: Gtk.main_quit() else: self.set_idle_timeout() def on_mnu_clone_activate(self, _menuitem): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) # TODO: what happens when we clone a folder? if "pasaffe_treenode." in entry_uuid: return else: self.clone_entry(entry_uuid) def on_username_copy_activate(self, _menuitem): self.copy_selected_entry_item(4) def on_password_copy_activate(self, _menuitem): self.copy_selected_entry_item(6) def on_url_copy_activate(self, _menuitem): self.copy_selected_entry_item(13) def on_copy_username_clicked(self, _toolbutton): self.copy_selected_entry_item(4) def on_copy_password_clicked(self, _toolbutton): self.copy_selected_entry_item(6) def on_display_secrets_toggled(self, toolbutton): is_active = toolbutton.get_active() self.display_secrets(is_active) self.ui.mnu_display_secrets.set_active(is_active) def on_mnu_display_secrets_toggled(self, menuitem): is_active = menuitem.get_active() self.display_secrets(is_active) self.ui.display_secrets.set_active(is_active) def display_secrets(self, display=True): self.set_idle_timeout() treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) # Bail out of we're a folder if "pasaffe_treenode." in entry_uuid: return self.display_data(entry_uuid, display) def copy_selected_entry_item(self, item): self.set_idle_timeout() treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) # Bail out of we're a folder if "pasaffe_treenode." in entry_uuid: return if item in self.passfile.records[entry_uuid]: for atom in [Gdk.SELECTION_CLIPBOARD, Gdk.SELECTION_PRIMARY]: clipboard = Gtk.Clipboard.get(atom) value = self.passfile.records[entry_uuid][item] length = len(value.encode('utf-8')) clipboard.set_text(value, length) self.last_copied = value clipboard.store() self.set_clipboard_timeout() def on_mnu_add_entry_activate(self, _menuitem): self.add_entry() def on_mnu_add_folder_activate(self, _menuitem): self.add_folder() def on_mnu_edit1_activate(self, _menuitem): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: self.edit_folder(treemodel, treeiter) else: self.edit_entry(entry_uuid) def on_mnu_delete_activate(self, _menuitem): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: self.remove_folder() else: self.remove_entry() def on_mnu_lock_activate(self, _menuitem): self.lock_screen() def on_mnu_info_activate(self, _menuitem): header = _('Database Information\n\n') information = _('Number of entries: %s\n') % \ len(self.passfile.records) information += '\n' if self.passfile.get_saved_name(): information += _('Last saved by: %s\n') % \ self.passfile.get_saved_name() if self.passfile.get_saved_host(): information += _('Last saved on host: %s\n') % \ self.passfile.get_saved_host() information += _('Last save date: %s\n') % \ self.passfile.get_saved_date_string() information += '\n' information += _('Database version: %s\n') % \ self.passfile.get_database_version_string() if self.passfile.get_saved_application(): information += _('Application used: %s\n') % \ self.passfile.get_saved_application() information += '\n' information += _('Database location:\n%s\n') % self.database information = header + GLib.markup_escape_text(information) info_dialog = Gtk.MessageDialog(transient_for=self, modal=True, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK) info_dialog.set_markup(information) info_dialog.run() info_dialog.destroy() def on_mnu_open_url_activate(self, _menuitem): self.open_url() def on_mnu_find_toggled(self, menuitem): is_active = menuitem.get_active() self.show_find(is_active) self.ui.toolbar_find.set_active(is_active) def on_toolbar_find_toggled(self, toolbutton): is_active = toolbutton.get_active() self.show_find(is_active) self.ui.mnu_find.set_active(is_active) def on_find_btn_close_clicked(self, _button): self.show_find(False) self.ui.toolbar_find.set_active(False) self.ui.mnu_find.set_active(False) def on_find_btn_prev_clicked(self, _button): self.update_find_results() self.goto_next_find_result(backwards=True) def on_find_btn_next_clicked(self, _button): self.update_find_results() self.goto_next_find_result() def update_find_results(self, force=False): if self.ui.find_box.get_property("visible") is False: return find = self.ui.find_entry.get_text() # Don't crash with stupid "bogus escape (end of line)" error while find[-1:] == '\\': find = find[:-1] self.passfile.update_find_results(find, force) def goto_next_find_result(self, backwards=False): uuid_hex = self.passfile.get_next_find_result(backwards) if uuid_hex is not None: self.goto_uuid(uuid_hex) def on_find_entry_activate(self, _entry): self.update_find_results() self.goto_next_find_result() def show_find(self, show): if self.ui.find_box.get_property("visible") == show: return if show is True: self.ui.find_entry.set_text("") self.find_value = "" self.ui.find_box.set_property("visible", True) self.ui.find_entry.grab_focus() else: self.ui.find_box.set_property("visible", False) self.find_results = [] self.find_results_index = None def on_open_url_clicked(self, _toolbutton): self.open_url() def on_mnu_chg_password_activate(self, _menuitem): success = False newpass_dialog = self.NewPasswordDialog() newpass_dialog.set_transient_for(self) while success is False: response = newpass_dialog.run() if response == Gtk.ResponseType.OK: old_password = newpass_dialog.ui.pass_entry1.get_text() password_a = newpass_dialog.ui.pass_entry2.get_text() password_b = newpass_dialog.ui.pass_entry3.get_text() if password_a != password_b: newpass_dialog.ui.label3.set_text( _("Passwords don't match! Please try again.")) newpass_dialog.ui.label3.set_property("visible", True) newpass_dialog.ui.pass_entry2.grab_focus() elif password_a == '': newpass_dialog.ui.label3.set_text( _("New password cannot be blank! Please try again.")) newpass_dialog.ui.label3.set_property("visible", True) newpass_dialog.ui.pass_entry2.grab_focus() elif not self.passfile.check_password(old_password): newpass_dialog.ui.label3.set_text( _("Old password is invalid! Please try again.")) newpass_dialog.ui.label3.set_property("visible", True) newpass_dialog.ui.pass_entry1.grab_focus() else: self.passfile.new_keys(password_a) self.set_save_status(True) self.save_db() success = True else: break newpass_dialog.destroy() def lock_screen(self): self.disable_idle_timeout() self.clear_clipboard() self.is_locked = True self.ui.pasaffe_vbox.reparent(self.ui.empty_window) self.ui.lock_vbox.reparent(self.ui.pasaffe_window) self.set_menu_sensitive(False) self.ui.lock_unlock_button.grab_focus() def on_pasaffe_window_delete_event(self, _window, event): # Pasaffe window is closing self.clear_clipboard() def on_lock_unlock_button_clicked(self, _button): success = False password_dialog = self.PasswordEntryDialog() password_dialog.set_transient_for(self) password_dialog.set_modal(True) while success is False: response = password_dialog.run() if response == Gtk.ResponseType.OK: password = password_dialog.ui.password_entry.get_text() success = self.passfile.check_password(password) if success is False: password_dialog.ui.password_error_label.set_property( "visible", True) password_dialog.ui.password_entry.set_text("") password_dialog.ui.password_entry.grab_focus() else: password_dialog.destroy() return password_dialog.destroy() self.ui.lock_vbox.reparent(self.ui.lock_window) self.ui.pasaffe_vbox.reparent(self.ui.pasaffe_window) self.set_menu_sensitive(True) self.is_locked = False self.set_idle_timeout() def on_lock_quit_button_clicked(self, _button): if self.save_warning() is False: Gtk.main_quit() return def set_menu_sensitive(self, status): # There's got to be a better way to do this self.ui.mnu_lock.set_sensitive(status) self.ui.mnu_chg_password.set_sensitive(status) self.ui.mnu_add_entry.set_sensitive(status) self.ui.mnu_add_folder.set_sensitive(status) self.ui.mnu_clone.set_sensitive(status) self.ui.mnu_delete.set_sensitive(status) self.ui.mnu_find.set_sensitive(status) self.ui.url_copy.set_sensitive(status) self.ui.username_copy.set_sensitive(status) self.ui.password_copy.set_sensitive(status) self.ui.mnu_preferences.set_sensitive(status) self.ui.mnu_open_url.set_sensitive(status) self.ui.mnu_info.set_sensitive(status) if status is False: self.ui.mnu_display_secrets.set_sensitive(False) else: self.set_show_password_status() if status is True and self.needs_saving is True: self.ui.mnu_save.set_sensitive(True) self.ui.save.set_sensitive(True) else: self.ui.mnu_save.set_sensitive(False) # Work around issue where button is insensitive, but icon is # not greyed out self.ui.save.set_sensitive(True) self.ui.save.set_sensitive(False) def set_menu_for_entry(self, status): # main menu self.ui.mnu_clone.set_sensitive(status) self.ui.url_copy.set_sensitive(status) self.ui.username_copy.set_sensitive(status) self.ui.password_copy.set_sensitive(status) self.ui.mnu_open_url.set_sensitive(status) # context menu self.ui.mnu_clone1.set_sensitive(status) self.ui.url_copy1.set_sensitive(status) self.ui.username_copy1.set_sensitive(status) self.ui.password_copy1.set_sensitive(status) # Toolbar self.ui.open_url.set_sensitive(status) self.ui.copy_username.set_sensitive(status) self.ui.copy_password.set_sensitive(status) if status is False: self.ui.mnu_display_secrets.set_sensitive(False) self.ui.display_secrets.set_sensitive(False) else: self.set_show_password_status() def on_add_clicked(self, _toolbutton): self.add_entry() def on_add_folder_clicked(self, _toolbutton): self.add_folder() def on_edit_clicked(self, _toolbutton): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: self.edit_folder(treemodel, treeiter) else: self.edit_entry(entry_uuid) def on_remove_clicked(self, _toolbutton): treemodel, treeiter = self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." in entry_uuid: self.remove_folder() else: self.remove_entry() def set_idle_timeout(self): if self.idle_id is not None: GLib.source_remove(self.idle_id) self.idle_id = None if (self.settings.get_boolean('lock-on-idle') is True and self.settings.get_int('idle-timeout') != 0): idle_time = int(self.settings.get_int('idle-timeout') * 1000 * 60) self.idle_id = GLib.timeout_add( idle_time, self.idle_timeout_reached) def idle_timeout_reached(self): if self.is_locked is False: self.lock_screen() if self.idle_id is not None: GLib.source_remove(self.idle_id) self.idle_id = None def disable_idle_timeout(self): if self.idle_id is not None: GLib.source_remove(self.idle_id) self.idle_id = None def set_clipboard_timeout(self): if self.clipboard_id is not None: GLib.source_remove(self.clipboard_id) self.clipboard_id = None if self.settings.get_int('clipboard-timeout') != 0: clipboard_time = int( self.settings.get_int('clipboard-timeout') * 1000) self.clipboard_id = GLib.timeout_add( clipboard_time, self.clipboard_timeout_reached) def clipboard_timeout_reached(self): self.clear_clipboard() def clear_clipboard(self): if self.clipboard_id is not None: GLib.source_remove(self.clipboard_id) self.clipboard_id = None found_copy_in_clipboard = False for atom in [Gdk.SELECTION_CLIPBOARD, Gdk.SELECTION_PRIMARY]: clipboard = Gtk.Clipboard.get(atom) text = clipboard.wait_for_text() if text is not None and self.last_copied is not None and \ text == self.last_copied: found_copy_in_clipboard = True if found_copy_in_clipboard: for atom in [Gdk.SELECTION_CLIPBOARD, Gdk.SELECTION_PRIMARY]: clipboard = Gtk.Clipboard.get(atom) clipboard.set_text("", 0) clipboard.store() self.last_copied = None def set_show_password_status(self): visible = self.settings.get_boolean('visible-secrets') if visible is True: self.ui.mnu_display_secrets.set_sensitive(False) # Work around issue where button is insensitive, but icon is # not greyed out self.ui.display_secrets.set_sensitive(True) self.ui.display_secrets.set_sensitive(False) else: self.ui.mnu_display_secrets.set_sensitive(True) self.ui.display_secrets.set_sensitive(True) def _set_title(self): prefix = "" if not self.default_database: prefix = "%s - " % os.path.basename(self.database) self.set_title("%s%sPasaffe" % ( prefix, "*" if self.needs_saving else "")) def set_save_status(self, needed): self.needs_saving = needed self.ui.save.set_sensitive(needed) self.ui.mnu_save.set_sensitive(needed) self._set_title() def get_save_status(self): return self.needs_saving ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613078.0 pasaffe-0.57/pasaffe/PasswordEntryDialog.py0000644000175000017500000000452200000000000022453 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 from pasaffe_lib.helpersgui import get_builder # noqa: E402 class PasswordEntryDialog(Gtk.Dialog): __gtype_name__ = "PasswordEntryDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated PasswordEntryDialog object. """ builder = get_builder('PasswordEntryDialog') new_object = builder.get_object('password_entry_dialog') new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called when we're finished initializing. finish_initalizing should be called after parsing the ui definition and creating a PasswordEntryDialog object with it in order to finish initializing the start of the new PasswordEntryDialog instance. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self) def on_btn_ok_clicked(self, widget, data=None): """The user has elected to save the changes. Called before the dialog returns Gtk.RESONSE_OK from run(). """ pass def on_btn_cancel_clicked(self, widget, data=None): """The user has elected cancel changes. Called before the dialog returns Gtk.ResponseType.CANCEL for run() """ pass if __name__ == "__main__": dialog = PasswordEntryDialog() dialog.show() Gtk.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613118.0 pasaffe-0.57/pasaffe/PreferencesPasaffeDialog.py0000644000175000017500000000554200000000000023361 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # from gi.repository import Gio import logging from pasaffe_lib.PreferencesDialog import PreferencesDialog logger = logging.getLogger('pasaffe') class PreferencesPasaffeDialog(PreferencesDialog): __gtype_name__ = "PreferencesPasaffeDialog" def finish_initializing(self, builder): # pylint: disable=E1002 """Set up the preferences dialog""" super(PreferencesPasaffeDialog, self).finish_initializing(builder) # Bind each preference widget to gsettings settings = Gio.Settings.new("net.launchpad.pasaffe") widget = self.builder.get_object('visible-secrets') settings.bind("visible-secrets", widget, "active", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('only-passwords-are-secret') settings.bind("only-passwords-are-secret", widget, "active", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('lock-on-idle') settings.bind("lock-on-idle", widget, "active", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('idle-timeout') settings.bind("idle-timeout", widget, "value", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('auto-save') settings.bind("auto-save", widget, "active", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('password-length') settings.bind("password-length", widget, "value", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('tree-expansion') settings.bind("tree-expansion", widget, "active-id", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('double-click') settings.bind("double-click", widget, "active-id", Gio.SettingsBindFlags.DEFAULT) widget = self.builder.get_object('display-usernames') settings.bind("display-usernames", widget, "active", Gio.SettingsBindFlags.DEFAULT) # Code for other initialization actions should be added here. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613887.0 pasaffe-0.57/pasaffe/SaveChangesDialog.py0000644000175000017500000000450200000000000022014 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 from pasaffe_lib.helpersgui import get_builder # noqa: E402 class SaveChangesDialog(Gtk.Dialog): __gtype_name__ = "SaveChangesDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated SaveChangesDialog object. """ builder = get_builder('SaveChangesDialog') new_object = builder.get_object('save_changes_dialog') new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called when we're finished initializing. finish_initalizing should be called after parsing the ui definition and creating a SaveChangesDialog object with it in order to finish initializing the start of the new SaveChangesDialog instance. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self) def on_btn_ok_clicked(self, widget, data=None): """The user has elected to save the changes. Called before the dialog returns Gtk.RESONSE_OK from run(). """ pass def on_btn_cancel_clicked(self, widget, data=None): """The user has elected cancel changes. Called before the dialog returns Gtk.ResponseType.CANCEL for run() """ pass if __name__ == "__main__": dialog = SaveChangesDialog() dialog.show() Gtk.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613787.0 pasaffe-0.57/pasaffe/__init__.py0000644000175000017500000000505200000000000020245 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import optparse import gettext t = gettext.translation('pasaffe', fallback=True) _ = t.gettext import gi # noqa: E402 gi.require_version('Gtk', '3.0') from gi.repository import Gio, Gtk # noqa: E402 from pasaffe import PasaffeWindow # noqa: E402 from pasaffe_lib import set_up_logging, get_version # noqa: E402 from pasaffe_lib.helpers import get_database_path # noqa: E402 def parse_options(): """Support for command line options""" parser = optparse.OptionParser(usage="Usage: %prog [filename] [options]", version="%%prog %s" % get_version()) parser.add_option( "-v", "--verbose", action="count", dest="verbose", help=_("Show debug messages (-vv debugs pasaffe_lib also)")) parser.add_option( "-f", "--file", dest="filename", help=_("use database FILE"), metavar="FILE") parser.add_option( "-s", "--set-default", dest="set_default", help=_("set database as default"), action='store_true') (options, args) = parser.parse_args() if args and options.filename is None: options.filename = args[0] set_up_logging(options) return options def main(): 'constructor for your class instances' options = parse_options() filename = get_database_path() settings = Gio.Settings.new("net.launchpad.pasaffe") # On first launch, set the standard location if settings.get_string('database-path') == "": settings.set_string('database-path', filename) # Override path that was saved with path from command line if options.set_default and options.filename is not None: settings.set_string('database-path', options.filename) # Run the application. window = PasaffeWindow.PasaffeWindow(database=options.filename) window.show() # pylint: disable=E1101 Gtk.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1494620248.0 pasaffe-0.57/pasaffe.desktop.in0000644000175000017500000000026500000000000020135 0ustar00mdeslaurmdeslaur00000000000000[Desktop Entry] _Name=Pasaffe _Comment=Pasaffe password manager Categories=GNOME;Utility; Exec=pasaffe %f Icon=pasaffe Terminal=false Type=Application MimeType=application/x-psafe3 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/pasaffe_lib/0000775000175000017500000000000000000000000016762 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614207.0 pasaffe-0.57/pasaffe_lib/AboutDialog.py0000644000175000017500000000361100000000000021525 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 from . helpersgui import get_builder # noqa: E402 class AboutDialog(Gtk.AboutDialog): __gtype_name__ = "AboutDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated AboutDialog object. """ builder = get_builder('AboutPasaffeDialog') new_object = builder.get_object("about_pasaffe_dialog") new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called while initializing this instance in __new__ finish_initalizing should be called after parsing the ui definition and creating a AboutDialog object with it in order to finish initializing the start of the new AboutPasaffeDialog instance. Put your initialization code in here and leave __init__ undefined. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1607700948.374914 pasaffe-0.57/pasaffe_lib/Builder.py0000644000175000017500000002645300000000000020732 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # '''Enhances builder connections, provides object to access glade objects''' import gi gi.require_version('Gtk', '3.0') from gi.repository import GObject, Gtk # noqa: E402 import inspect # noqa: E402 import functools # noqa: E402 import logging # noqa: E402 logger = logging.getLogger('pasaffe_lib') from xml.etree.cElementTree import ElementTree # noqa: E402 # this module is big so uses some conventional prefixes and postfixes # *s list, except self.widgets is a dictionary # *_dict dictionary # *name string # ele_* element in a ElementTree # pylint: disable=R0904 # the many public methods is a feature of Gtk.Builder class Builder(Gtk.Builder): ''' extra features connects glade defined handler to default_handler if necessary auto connects widget to handler with matching name or alias auto connects several widgets to a handler via multiple aliases allow handlers to lookup widget name logs every connection made, and any on_* not made ''' def __init__(self): Gtk.Builder.__init__(self) self.widgets = {} self.glade_handler_dict = {} self.connections = [] self._reverse_widget_dict = {} # pylint: disable=R0201 # this is a method so that a subclass of Builder can redefine it def default_handler(self, handler_name, filename, *args, **kwargs): '''helps the apprentice guru glade defined handlers that do not exist come here instead. An apprentice guru might wonder which signal does what he wants, now he can define any likely candidates in glade and notice which ones get triggered when he plays with the project. this method does not appear in Gtk.Builder''' logger.debug('''tried to call non-existent function:%s() expected in %s args:%s kwargs:%s''', handler_name, filename, args, kwargs) # pylint: enable=R0201 def get_name(self, widget): ''' allows a handler to get the name (id) of a widget this method does not appear in Gtk.Builder''' return self._reverse_widget_dict.get(widget) def add_from_file(self, filename): '''parses xml file and stores wanted details''' Gtk.Builder.add_from_file(self, filename) # extract data for the extra interfaces tree = ElementTree() tree.parse(filename) ele_widgets = tree.iter("object") for ele_widget in ele_widgets: name = ele_widget.attrib['id'] widget = self.get_object(name) # populate indexes - a dictionary of widgets self.widgets[name] = widget # populate a reversed dictionary self._reverse_widget_dict[widget] = name # populate connections list ele_signals = ele_widget.findall("signal") connections = [ (name, ele_signal.attrib['name'], ele_signal.attrib['handler']) for ele_signal in ele_signals] if connections: self.connections.extend(connections) ele_signals = tree.iter("signal") for ele_signal in ele_signals: self.glade_handler_dict.update( {ele_signal.attrib["handler"]: None}) def connect_signals(self, callback_obj): '''connect the handlers defined in glade reports successful and failed connections and logs call to missing handlers''' filename = inspect.getfile(callback_obj.__class__) callback_handler_dict = dict_from_callback_obj(callback_obj) connection_dict = {} connection_dict.update(self.glade_handler_dict) connection_dict.update(callback_handler_dict) for item in list(connection_dict.items()): if item[1] is None: # the handler is missing so reroute to default_handler handler = functools.partial( self.default_handler, item[0], filename) connection_dict[item[0]] = handler # replace the run time warning logger.warn("expected handler '%s' in %s", item[0], filename) # connect glade define handlers Gtk.Builder.connect_signals(self, connection_dict) # let's tell the user how we applied the glade design for connection in self.connections: widget_name, signal_name, handler_name = connection logger.debug("connect builder by design '%s', '%s', '%s'", widget_name, signal_name, handler_name) def get_ui(self, callback_obj=None, by_name=True): '''Creates the ui object with widgets as attributes connects signals by 2 methods this method does not appear in Gtk.Builder''' result = UiFactory(self.widgets) # Hook up any signals the user defined in glade if callback_obj is not None: # connect glade define handlers self.connect_signals(callback_obj) if by_name: auto_connect_by_name(callback_obj, self) return result # pylint: disable=R0903 # this class deliberately does not provide any public interfaces # apart from the glade widgets class UiFactory(): ''' provides an object with attributes as glade widgets''' def __init__(self, widget_dict): self._widget_dict = widget_dict for (widget_name, widget) in list(widget_dict.items()): setattr(self, widget_name, widget) # Mangle any non-usable names (like with spaces or dashes) # into pythonic ones cannot_message = """cannot bind ui.%s, name already exists consider using a pythonic name instead of design name '%s'""" consider_message = """consider using a pythonic name instead""" + \ """ of design name '%s'""" for (widget_name, widget) in list(widget_dict.items()): pyname = make_pyname(widget_name) if pyname != widget_name: if hasattr(self, pyname): logger.debug(cannot_message, pyname, widget_name) else: logger.debug(consider_message, widget_name) setattr(self, pyname, widget) def iterator(): '''Support 'for o in self' ''' return iter(list(widget_dict.values())) setattr(self, '__iter__', iterator) def __getitem__(self, name): 'access as dictionary where name might be non-pythonic' return self._widget_dict[name] # pylint: enable=R0903 def make_pyname(name): ''' mangles non-pythonic names into pythonic ones''' pyname = '' for character in name: if (character.isalpha() or character == '_' or (pyname and character.isdigit())): pyname += character else: pyname += '_' return pyname # Until bug https://bugzilla.gnome.org/show_bug.cgi?id=652127 is fixed, we # need to reimplement inspect.getmembers. Gobject introspection doesn't # play nice with it. def getmembers(obj, check): members = [] for k in dir(obj): try: attr = getattr(obj, k) except: # noqa: E722 continue if check(attr): members.append((k, attr)) members.sort() return members def dict_from_callback_obj(callback_obj): '''a dictionary interface to callback_obj''' methods = getmembers(callback_obj, inspect.ismethod) aliased_methods = [x[1] for x in methods if hasattr(x[1], 'aliases')] # a method may have several aliases # @alias('on_btn_foo_clicked') # @alias('on_tool_foo_activate') # on_menu_foo_activate(): # pass alias_groups = [(x.aliases, x) for x in aliased_methods] aliases = [] for item in alias_groups: for alias in item[0]: aliases.append((alias, item[1])) dict_methods = dict(methods) dict_aliases = dict(aliases) results = {} results.update(dict_methods) results.update(dict_aliases) return results def auto_connect_by_name(callback_obj, builder): '''finds handlers like on__ and connects them i.e. find widget,signal pair in builder and call widget.connect(signal, on__)''' callback_handler_dict = dict_from_callback_obj(callback_obj) for item in list(builder.widgets.items()): (widget_name, widget) = item signal_ids = [] try: widget_type = type(widget) while widget_type: signal_ids.extend(GObject.signal_list_ids(widget_type)) widget_type = GObject.type_parent(widget_type) except RuntimeError: # pylint wants a specific error pass signal_names = [GObject.signal_name(sid) for sid in signal_ids] # Now, automatically find any the user didn't specify in glade for sig in signal_names: # using convention suggested by glade handler_names = ["on_%s_%s" % (widget_name, sig)] # Using the convention that the top level window is not # specified in the handler name. That is use # on_destroy() instead of on_windowname_destroy() if widget is callback_obj: handler_names.append("on_%s" % sig) do_connect(item, sig, handler_names, callback_handler_dict, builder.connections) log_unconnected_functions(callback_handler_dict, builder.connections) def do_connect(item, signal_name, handler_names, callback_handler_dict, connections): '''connect this signal to an unused handler''' widget_name, widget = item for handler_name in handler_names: target = handler_name in list(callback_handler_dict.keys()) connection = (widget_name, signal_name, handler_name) duplicate = connection in connections if target and not duplicate: widget.connect(signal_name, callback_handler_dict[handler_name]) connections.append(connection) logger.debug("connect builder by name '%s','%s', '%s'", widget_name, signal_name, handler_name) def log_unconnected_functions(callback_handler_dict, connections): '''log functions like on_* that we could not connect''' connected_functions = [x[2] for x in connections] handler_names = list(callback_handler_dict.keys()) unconnected = [x for x in handler_names if x.startswith('on_')] for handler_name in connected_functions: try: unconnected.remove(handler_name) except ValueError: pass for handler_name in unconnected: logger.debug("Not connected to builder '%s'", handler_name) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614611.0 pasaffe-0.57/pasaffe_lib/PreferencesDialog.py0000644000175000017500000000454700000000000022725 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # """this dialog adjusts values in gsettings """ import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 import logging # noqa: E402 logger = logging.getLogger('pasaffe_lib') from . helpers import get_help_uri # noqa: E402 from . helpersgui import get_builder, show_uri # noqa: E402 class PreferencesDialog(Gtk.Dialog): __gtype_name__ = "PreferencesDialog" def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated PreferencesDialog object. """ builder = get_builder('PreferencesPasaffeDialog') new_object = builder.get_object("preferences_pasaffe_dialog") new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called while initializing this instance in __new__ finish_initalizing should be called after parsing the ui definition and creating a PreferencesDialog object with it in order to finish initializing the start of the new PerferencesPasaffeDialog instance. Put your initialization code in here and leave __init__ undefined. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self, True) # code for other initialization actions should be added here def on_btn_close_clicked(self, widget, data=None): self.destroy() def on_btn_help_clicked(self, widget, data=None): show_uri(self, get_help_uri('preferences')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614274.0 pasaffe-0.57/pasaffe_lib/Window.py0000644000175000017500000001264500000000000020611 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import gi gi.require_version('Gtk', '3.0') from gi.repository import Gio, Gtk # noqa: E402 import logging # noqa: E402 logger = logging.getLogger('pasaffe_lib') from . helpers import get_help_uri # noqa: E402 from . helpersgui import get_builder, show_uri # noqa: E402 # This class is meant to be subclassed by PasaffeWindow. It provides # common functions and some boilerplate. class Window(Gtk.Window): __gtype_name__ = "Window" # To construct a new instance of this method, the following notable # methods are called in this order: # __new__(cls) # __init__(self) # finish_initializing(self, builder) # __init__(self) # # For this reason, it's recommended you leave __init__ empty and put # your initialization code in finish_initializing def __new__(cls): """Special static method that's automatically called by Python when constructing a new instance of this class. Returns a fully instantiated BasePasaffeWindow object. """ builder = get_builder('PasaffeWindow') new_object = builder.get_object("pasaffe_window") new_object.finish_initializing(builder) return new_object def finish_initializing(self, builder): """Called while initializing this instance in __new__ finish_initializing should be called after parsing the UI definition and creating a PasaffeWindow object with it in order to finish initializing the start of the new PasaffeWindow instance. """ # Get a reference to the builder and set up the signals. self.builder = builder self.ui = builder.get_ui(self, True) self.PreferencesDialog = None # class self.preferences_dialog = None # instance self.AboutDialog = None # class self.settings = Gio.Settings.new("net.launchpad.pasaffe") self.settings.connect('changed', self.on_preferences_changed) def on_mnu_contents_activate(self, widget, data=None): show_uri(self, get_help_uri()) def on_mnu_about_activate(self, widget, data=None): """Display the about box for pasaffe.""" if self.AboutDialog is not None: about = self.AboutDialog() # pylint: disable=E1102 about.set_transient_for(self) about.run() about.destroy() def on_mnu_preferences_activate(self, widget, data=None): """Display the preferences window for pasaffe.""" """ From the PyGTK Reference manual Say for example the preferences dialog is currently open, and the user chooses Preferences from the menu a second time; use the present() method to move the already-open dialog where the user can see it.""" if self.preferences_dialog is not None: logger.debug('show existing preferences_dialog') self.preferences_dialog.present() elif self.PreferencesDialog is not None: logger.debug('create new preferences_dialog') self.preferences_dialog = \ self.PreferencesDialog() # pylint: disable=E1102 self.preferences_dialog.set_transient_for(self) self.preferences_dialog.connect( 'destroy', self.on_preferences_dialog_destroyed) self.preferences_dialog.show() # destroy command moved into dialog to allow for a help button def on_mnu_close_activate(self, widget, data=None): """Signal handler for closing the PasaffeWindow.""" self.destroy() def on_destroy(self, widget, data=None): """Called when the PasaffeWindow is closed.""" # Clean up code for saving application state should be added here. Gtk.main_quit() def on_preferences_changed(self, settings, key, data=None): logger.debug('preference changed: %s = %s' % (key, str(settings.get_value(key)))) if key == 'visible-secrets': self.set_show_password_status() treemodel, treeiter = \ self.ui.treeview1.get_selection().get_selected() if treeiter is not None: entry_uuid = treemodel.get_value(treeiter, 2) if "pasaffe_treenode." not in entry_uuid: self.display_data(entry_uuid, show_secrets=settings.get_boolean(key)) def on_preferences_dialog_destroyed(self, widget, data=None): '''only affects gui logically there is no difference between the user closing, minimising or ignoring the preferences dialog''' logger.debug('on_preferences_dialog_destroyed') # to determine whether to create or present preferences_dialog self.preferences_dialog = None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614506.0 pasaffe-0.57/pasaffe_lib/__init__.py0000644000175000017500000000166300000000000021077 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # '''facade - makes pasaffe_lib package easy to refactor while keeping its api constant''' from . helpers import set_up_logging # noqa: F401 from . pasaffeconfig import get_version # noqa: F401 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614521.0 pasaffe-0.57/pasaffe_lib/blowfish.py0000644000175000017500000007550000000000000021156 0ustar00mdeslaurmdeslaur00000000000000# # blowfish.py # Copyright (C) 2002 Michael Gilfix # # This module is open source; you can redistribute it and/or # modify it under the terms of the GPL or Artistic License. # These licenses are available at http://www.opensource.org # # This software must be used and distributed in accordance # with the law. The author claims no liability for its # misuse. # # 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. # # This software was modified by Ivan Voras: CTR cipher mode of # operation was added, together with testing and example code. # These changes are (c) 2007./08. Ivan Voras # These changes can be used, modified ad distributed under the # GPL or Artistic License, the same as the original module. # All disclaimers of warranty from the original module also # apply to these changes. # CBC mode contributed by Joel Edwards , # under the same license conditions. """ Blowfish Encryption This module is a pure python implementation of Bruce Schneier's encryption scheme 'Blowfish'. Blowish is a 16-round Feistel Network cipher and offers substantial speed gains over DES. The key is a string of length anywhere between 64 and 448 bits, or equivalently 8 and 56 bytes. The encryption and decryption functions operate on 64-bit blocks, or 8 byte strings. Send questions, comments, bugs my way: Michael Gilfix The module has been expanded to include CTR stream encryption/decryption mode, built from the primitives from the orignal module. This change did not alter any of the base Blowfish code from the original author. The author of CTR changes is: Ivan Voras """ import struct __author__ = "Michael Gilfix " class Blowfish: """Blowfish encryption Scheme This class implements the encryption and decryption functionality of the Blowfish cipher. Public functions: def __init__ (self, key) Creates an instance of blowfish using 'key' as the encryption key. Key is a string of length ranging from 8 to 56 bytes (64 to 448 bits). Once the instance of the object is created, the key is no longer necessary. def encrypt (self, data): Encrypt an 8 byte (64-bit) block of text where 'data' is an 8 byte string. Returns an 8-byte encrypted string. def decrypt (self, data): Decrypt an 8 byte (64-bit) encrypted block of text, where 'data' is the 8 byte encrypted string. Returns an 8-byte string of plaintext. def cipher (self, xl, xr, direction): Encrypts a 64-bit block of data where xl is the upper 32-bits and xr is the lower 32-bits. 'direction' is the direction to apply the cipher, either ENCRYPT or DECRYPT constants. returns a tuple of either encrypted or decrypted data of the left half and right half of the 64-bit block. def initCTR(self): Initializes CTR engine for encryption or decryption. def encryptCTR(self, data): Encrypts an arbitrary string and returns the encrypted string. The method can be called successively for multiple string blocks. def decryptCTR(self, data): Decrypts a string encrypted with encryptCTR() and returns the decrypted string. Private members: def __round_func (self, xl) Performs an obscuring function on the 32-bit block of data 'xl', which is the left half of the 64-bit block of data. Returns the 32-bit result as a long integer. """ # Cipher directions ENCRYPT = 0 DECRYPT = 1 # For the __round_func modulus = int(2) ** 32 def __init__(self, key): if not key or len(key) < 8 or len(key) > 56: raise RuntimeError("Attempted to initialize Blowfish cipher" " with key of invalid length: %s" % len(key)) self.p_boxes = [ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B ] self.s_boxes = [ [ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A ], [ 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 ], [ 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 ], [ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 ] ] # Cycle through the p-boxes and round-robin XOR the # key with the p-boxes key_len = len(key) index = 0 for i in range(len(self.p_boxes)): val = (key[index % key_len] << 24) + \ (key[(index + 1) % key_len] << 16) + \ (key[(index + 2) % key_len] << 8) + \ key[(index + 3) % key_len] self.p_boxes[i] = self.p_boxes[i] ^ val index = index + 4 # For the chaining process l, r = 0, 0 # Begin chain replacing the p-boxes for i in range(0, len(self.p_boxes), 2): l, r = self.cipher(l, r, self.ENCRYPT) self.p_boxes[i] = l self.p_boxes[i + 1] = r # Chain replace the s-boxes for i in range(len(self.s_boxes)): for j in range(0, len(self.s_boxes[i]), 2): l, r = self.cipher(l, r, self.ENCRYPT) self.s_boxes[i][j] = l self.s_boxes[i][j + 1] = r self.initCTR() def cipher(self, xl, xr, direction): """Encryption primitive""" if direction == self.ENCRYPT: for i in range(16): xl = xl ^ self.p_boxes[i] xr = self.__round_func(xl) ^ xr xl, xr = xr, xl xl, xr = xr, xl xr = xr ^ self.p_boxes[16] xl = xl ^ self.p_boxes[17] else: for i in range(17, 1, -1): xl = xl ^ self.p_boxes[i] xr = self.__round_func(xl) ^ xr xl, xr = xr, xl xl, xr = xr, xl xr = xr ^ self.p_boxes[1] xl = xl ^ self.p_boxes[0] return xl, xr def __round_func(self, xl): a = (xl & 0xFF000000) >> 24 b = (xl & 0x00FF0000) >> 16 c = (xl & 0x0000FF00) >> 8 d = xl & 0x000000FF # Perform all ops as longs then and out the last 32-bits to # obtain the integer f = (int(self.s_boxes[0][a]) + int(self.s_boxes[1][b])) % self.modulus f = f ^ int(self.s_boxes[2][c]) f = f + int(self.s_boxes[3][d]) f = (f % self.modulus) & 0xFFFFFFFF return f def encrypt(self, data): if not len(data) == 8: raise RuntimeError("Attempted to encrypt data of invalid block" " length: %s" % len(data)) # Use big endianess since that's what everyone else uses xl = data[3] | (data[2] << 8) | (data[1] << 16) | \ (data[0] << 24) xr = data[7] | (data[6] << 8) | (data[5] << 16) | \ (data[4] << 24) cl, cr = self.cipher(xl, xr, self.ENCRYPT) chars = bytearray() chars.append((cl >> 24) & 0xFF) chars.append((cl >> 16) & 0xFF) chars.append((cl >> 8) & 0xFF) chars.append(cl & 0xFF) chars.append((cr >> 24) & 0xFF) chars.append((cr >> 16) & 0xFF) chars.append((cr >> 8) & 0xFF) chars.append(cr & 0xFF) return bytes(chars) def decrypt(self, data): if not len(data) == 8: raise RuntimeError("Attempted to encrypt data of invalid block" " length: %s" % len(data)) # Use big endianess since that's what everyone else uses cl = data[3] | (data[2] << 8) | \ (data[1] << 16) | (data[0] << 24) cr = data[7] | (data[6] << 8) | \ (data[5] << 16) | (data[4] << 24) xl, xr = self.cipher(cl, cr, self.DECRYPT) chars = bytearray() chars.append((xl >> 24) & 0xFF) chars.append((xl >> 16) & 0xFF) chars.append((xl >> 8) & 0xFF) chars.append(xl & 0xFF) chars.append((xr >> 24) & 0xFF) chars.append((xr >> 16) & 0xFF) chars.append((xr >> 8) & 0xFF) chars.append(xr & 0xFF) return bytes(chars) # ==== CBC Mode ==== def initCBC(self, iv=0): """Initializes CBC mode of the cypher""" assert struct.calcsize("Q") == self.block_size() self.cbc_iv = struct.pack("Q", iv) def encryptCBC(self, data): """ Encrypts a buffer of data using CBC mode. Multiple successive buffers (belonging to the same logical stream of buffers) can be encrypted with this method one after the other without any intermediate work. Each buffer must be a multiple of 8-octets (64-bits) in length. """ if type(data) != bytes: raise RuntimeError("Can only work on 8-bit strings") if (len(data) % 8) != 0: raise RuntimeError("Can only work with data in 64-bit" " multiples in CBC mode") def xor(t): return t[0] ^ t[1] result = b'' block_size = self.block_size() for i in range(0, len(data), block_size): p_block = data[i:i + block_size] pair = list(zip(p_block, self.cbc_iv)) j_block = bytes(list(map(xor, pair))) c_block = self.encrypt(j_block) result += c_block self.cbc_iv = c_block return result def decryptCBC(self, data): if type(data) != bytes: raise RuntimeError("Can only work on 8-bit strings") if (len(data) % 8) != 0: raise RuntimeError("Can only work with data in 64-bit" " multiples in CBC mode") def xor(t): return t[0] ^ t[1] result = b'' block_size = self.block_size() for i in range(0, len(data), block_size): c_block = data[i:i + block_size] j_block = self.decrypt(c_block) pair = list(zip(j_block, self.cbc_iv)) p_block = bytes(list(map(xor, pair))) result += p_block self.cbc_iv = c_block return result # ==== CTR Mode ==== def initCTR(self, iv=0): """Initializes CTR mode of the cypher""" assert struct.calcsize("Q") == self.block_size() self.ctr_iv = iv self._calcCTRBUF() def _calcCTRBUF(self): """Calculates one block of CTR keystream""" # keystream block self.ctr_cks = self.encrypt(struct.pack("Q", self.ctr_iv)) self.ctr_iv += 1 self.ctr_pos = 0 def _nextCTRByte(self): """Returns one byte of CTR keystream""" b = self.ctr_cks[self.ctr_pos] self.ctr_pos += 1 if self.ctr_pos >= len(self.ctr_cks): self._calcCTRBUF() return b def encryptCTR(self, data): """ Encrypts a buffer of data with CTR mode. Multiple successive buffers (belonging to the same logical stream of buffers) can be encrypted with this method one after the other without any intermediate work. """ if type(data) != bytes: raise Exception("Can only work on 8-bit strings") result = bytearray() for ch in data: result.append(ch ^ self._nextCTRByte()) return bytes(result) def decryptCTR(self, data): return self.encryptCTR(data) def block_size(self): return 8 def key_length(self): return 56 def key_bits(self): return 56 * 8 @staticmethod def testVectors(): import binascii # for more vectors see http://www.schneier.com/code/vectors.txt vectors = ( (b'0000000000000000', b'0000000000000000', b'4EF997456198DD78'), (b'FFFFFFFFFFFFFFFF', b'FFFFFFFFFFFFFFFF', b'51866FD5B85ECB8A'), (b'3000000000000000', b'1000000000000001', b'7D856F9A613063F2'), (b'1111111111111111', b'1111111111111111', b'2466DD878B963C9D'), (b'49E95D6D4CA229BF', b'02FE55778117F12A', b'CF9C5D7A4986ADB5'), (b'E0FEE0FEF1FEF1FE', b'0123456789ABCDEF', b'C39E072D9FAC631D'), (b'07A7137045DA2A16', b'3BDD119049372802', b'2EEDDA93FFD39C79'), ) ok = True for v in vectors: c = Blowfish(binascii.a2b_hex(v[0])) e = binascii.b2a_hex(c.encrypt(binascii.a2b_hex(v[1]))).upper() if e != v[2]: print("VECTOR TEST FAIL: expecting %s, got %s" % (repr(v), e)) ok = False return ok ############################################################## # Module testing if __name__ == '__main__': if not Blowfish.testVectors(): print("WARNING: The implementation doesn't pass " "algorithm test vectors!") else: print("The implementation passes algorithm test vectors (ECB).") key = b'This is a test key' cipher = Blowfish(key) print("Testing encryption:") xl = 123456 xr = 654321 print("\tPlain text: (%s, %s)" % (xl, xr)) cl, cr = cipher.cipher(xl, xr, cipher.ENCRYPT) print("\tCrypted is: (%s, %s)" % (cl, cr)) dl, dr = cipher.cipher(cl, cr, cipher.DECRYPT) print("\tUnencrypted is: (%s, %s)" % (dl, dr)) print("Testing block encrypt:") text = b'testtest' print("\tText:\t\t%s" % text) crypted = cipher.encrypt(text) print("\tEncrypted:\t%s" % repr(crypted)) decrypted = cipher.decrypt(crypted) print("\tDecrypted:\t%s" % decrypted) print("Testing CTR encrypt:") cipher.initCTR() text = b"The quick brown fox jumps over the lazy dog" print("\tText:\t\t", text) crypted = cipher.encryptCTR(text) print("\tEncrypted:\t", repr(crypted)) cipher.initCTR() decrypted = cipher.decryptCTR(crypted) print("\tDecrypted:\t", decrypted) print("Testing CBC encrypt:") cipher.initCBC() text = b"Owen's Ornery Old Oryx Obstructed Olga's Optics." print("\tText:\t\t", text) crypted = cipher.encryptCBC(text) print("\tEncrypted:\t", repr(crypted)) cipher.initCBC() decrypted = cipher.decryptCBC(crypted) print("\tDecrypted:\t", decrypted) print("Testing speed") from time import time t1 = time() n = 0 tlen = 0 while True: for i in range(1000): tstr = "The quick brown fox jumps over the lazy dog %d" % i tbts = tstr.encode('utf-8') enc = cipher.encryptCTR(tbts) tlen += len(tbts) n += 1000 t2 = time() if t2 - t1 > 5: break t = t2 - t1 print("%d encryptions in %0.1f seconds:" % (n, t) + " %0.1f enc/s, %0.1f bytes/s" % (n / t, tlen / t)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572613917.0 pasaffe-0.57/pasaffe_lib/figaroxml.py0000644000175000017500000000521000000000000021320 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2012 Francesco Marella # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import struct import os import time from binascii import hexlify from xml.etree import cElementTree as ET import logging logger = logging.getLogger('pasaffe_lib') class FigaroXML: def __init__(self, filename=None): """ Reads a FPM2 file""" self.records = {} self.skipped = [] self.index = 0 self.cipher = None if filename is not None: self.readfile(filename) def readfile(self, filename): """ Parses database file""" try: element = ET.parse(filename) except Exception: raise RuntimeError("Could not open %s. Aborting." % filename) if element.getroot().tag != 'FPM': raise RuntimeError("Not a valid FPM2 XML file") for pwitem in element.findall('./PasswordList/PasswordItem'): uuid = os.urandom(16) uuid_hex = hexlify(uuid).decode('utf-8') timestamp = struct.pack(" # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import struct import hashlib import os import time from binascii import hexlify from . import blowfish import logging logger = logging.getLogger('pasaffe_lib') class GPassFile: def __init__(self, filename=None, password=None): '''Reads a GPass file''' self.records = {} self.gpass_iv = b"\x05\x17\x01\x7b\x0c\x03\x36\x5e" self.decoded_db = None self.index = 0 self.imported_folders = {} self.empty_folders = [] self.cipher = None if filename is not None: self._readfile(filename, password) def _parse_entry(self, entry_id, parent_id, entry_data): '''Parse an entry''' uuid = os.urandom(16) uuid_hex = hexlify(uuid).decode('utf-8') timestamp = struct.pack(" # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # """Helpers for an Ubuntu application.""" import logging import os import subprocess from . pasaffeconfig import get_data_file from . pasaffeconfig import get_help_prefix from . pasaffeconfig import get_help_separator import gettext from gettext import gettext as _ gettext.textdomain('pasaffe') class PathEntry: def __init__(self, name, uuid, path): self.name = name self.uuid = uuid self.path = path def _mycmp(self, other): # First, we sort by path result = self._sort_path(self.path, other.path) if result != 0: return result # Then we sort by name result = self._sort_name(self.name, other.name) return result def __lt__(self, other): return self._mycmp(other) < 0 def __gt__(self, other): return self._mycmp(other) > 0 def __eq__(self, other): return self._mycmp(other) == 0 def __le__(self, other): return self._mycmp(other) <= 0 def __ge__(self, other): return self._mycmp(other) >= 0 def __ne__(self, other): return self._mycmp(other) != 0 def _lower(self, name): # Doing .lower() is wrong in Python 2 as it doesn't properly # handle certain characters in Unicode languages. Unfortunately, # I can't think of a better solution right now, and it will work # properly once we migrate to Python 3 if name: return name.lower() else: return "" def _sort_name(self, first, second): # Perform a case-insensitive sort nocase_first = self._lower(first) nocase_second = self._lower(second) # We assume empty names are folders, so they need to # lose to be first in the list if nocase_first in (None, "") and nocase_second in (None, ""): return 0 elif nocase_first < nocase_second: return -1 elif nocase_first > nocase_second: return 1 else: # If they are the same when they are case-insensitive, we now # want to sort in a case-sensitive way if first < second: return -1 elif first > second: return 1 else: return 0 def _sort_path(self, first, second): # Folders should be displayed first, so they should lose to # entries that don't have folders if first in (None, []) and second in (None, []): return 0 elif first in (None, []): return 1 elif second in (None, []): return -1 elif len(first) < len(second): i = 0 for path in first: if not len(path): return 1 if not len(second[i]): return -1 # First test in a case insensitive way if self._lower(path) < self._lower(second[i]): return -1 if self._lower(path) > self._lower(second[i]): return 1 # Now try in a case-sensitive way if path < second[i]: return -1 if path > second[i]: return 1 i += 1 return 1 elif len(first) > len(second): i = 0 for path in second: if not len(path): return -1 if not len(first[i]): return 1 # First test in a case insensitive way if self._lower(path) > self._lower(first[i]): return -1 if self._lower(path) < self._lower(first[i]): return 1 # Now try in a case-sensitive way if path > first[i]: return -1 if path < first[i]: return 1 i += 1 return -1 else: i = 0 for path in first: if not len(path): return 1 if not len(second[i]): return -1 # First test in a case insensitive way if self._lower(path) < self._lower(second[i]): return -1 if self._lower(path) > self._lower(second[i]): return 1 # Now try in a case-sensitive way if path < second[i]: return -1 if path > second[i]: return 1 i += 1 return 0 def __repr__(self): return repr((self.name, self.uuid, self.path)) # Owais Lone : To get quick access to icons and stuff. def get_media_file(media_file_name): media_filename = get_data_file('media', '%s' % (media_file_name,)) if not os.path.exists(media_filename): media_filename = None return "file:///" + media_filename class NullHandler(logging.Handler): def emit(self, record): pass def set_up_logging(opts): # add a handler to prevent basicConfig root = logging.getLogger() null_handler = NullHandler() root.addHandler(null_handler) formatter = logging.Formatter("%(levelname)s:%(name)s: %(funcName)s()" " '%(message)s'") logger = logging.getLogger('pasaffe') logger_sh = logging.StreamHandler() logger_sh.setFormatter(formatter) logger.addHandler(logger_sh) lib_logger = logging.getLogger('pasaffe_lib') lib_logger_sh = logging.StreamHandler() lib_logger_sh.setFormatter(formatter) lib_logger.addHandler(lib_logger_sh) # Set the logging level to show debug messages. if opts.verbose: logger.setLevel(logging.DEBUG) logger.debug('logging enabled') if opts.verbose and opts.verbose > 1: lib_logger.setLevel(logging.DEBUG) def get_help_uri(page=None): # help_uri from source tree - default language here = os.path.dirname(__file__) help_uri = os.path.abspath(os.path.join(here, '..', 'help', 'C')) prefix = 'ghelp:' separator = '#' if not os.path.exists(help_uri): # installed so use gnome help tree - user's language help_uri = 'pasaffe' prefix = get_help_prefix() separator = get_help_separator() # unspecified page is the index.page if page is not None: help_uri = '%s%s%s' % (help_uri, separator, page) return '%s%s' % (prefix, help_uri) def alias(alternative_function_name): '''see http://www.drdobbs.com/web-development/184406073#l9''' def decorator(function): '''attach alternative_function_name(s) to function''' if not hasattr(function, 'aliases'): function.aliases = [] function.aliases.append(alternative_function_name) return function return decorator def folder_list_to_field(folder_list): '''Converts a folder list to a folder field''' field = "" if folder_list is None: return field for folder in folder_list: if field != "": field += "." field += folder.replace(".", "\\.") return field def field_to_folder_list(field): '''Converts a folder field to a folder list''' # We need to split into folders using the "." character, but not # if it is escaped with a \ folders = [] if field == "": return folders index = 0 location = 0 while index < len(field): location = field.find(".", location + 1) if location == -1: break if field[location - 1] == "\\": continue folders.append(field[index:location].replace("\\", '')) index = location + 1 folders.append(field[index:len(field)].replace('\\', '')) return folders def folder_list_to_path(folders, index=None): '''Converts a folder list to a folder path''' if len(folders) == 0 or folders is None: return "/" if index is None: index = len(folders) folder_path = "" for folder in folders[0:index + 1]: folder_path += "/" folder_path += folder.replace("/", "\\/") folder_path += "/" return folder_path def folder_path_to_list(folder_path): '''Converts a folder path to a folder list''' folders = [] if folder_path.endswith("/"): folder_path = folder_path[:-1] if folder_path.startswith("/"): folder_path = folder_path[1:] if folder_path == '': return folders # We need to split into folders using the "/" character, but not # if it is escaped with a \ index = 0 location = 0 while index < len(folder_path): location = folder_path.find("/", location + 1) if location == -1: break if folder_path[location - 1] == "\\": continue folders.append(folder_path[index:location].replace("\\", '')) index = location + 1 folders.append(folder_path[index:len(folder_path)].replace('\\', '')) return folders def confirm(prompt=None, resp=False): """prompts for yes or no response from the user. Returns True for yes and False for no. 'resp' should be set to the default value assumed by the caller when user simply types ENTER. >>> confirm(prompt='Create Directory?', resp=True) Create Directory? [y]|n: True >>> confirm(prompt='Create Directory?', resp=False) Create Directory? [n]|y: False >>> confirm(prompt='Create Directory?', resp=False) Create Directory? [n]|y: y True """ if prompt is None: prompt = 'Confirm' if resp: prompt = '%s [%s]|%s: ' % (prompt, 'y', 'n') else: prompt = '%s [%s]|%s: ' % (prompt, 'n', 'y') while True: ans = input(prompt) if not ans: return resp if ans not in ['y', 'Y', 'n', 'N']: print('please enter y or n.') continue if ans == 'y' or ans == 'Y': return True if ans == 'n' or ans == 'N': return False def get_database_path(): """Determines standard XDG location for database""" if 'XDG_DATA_HOME' in os.environ: basedir = os.path.join(os.environ['XDG_DATA_HOME'], 'pasaffe') else: basedir = os.path.join(os.environ['HOME'], '.local/share/pasaffe') if not os.path.exists(basedir): os.makedirs(basedir, 0o700) return os.path.join(basedir, 'pasaffe.psafe3') def gen_password(number, size): """Generate new passwords, each of size """ command = ["apg", "-a", "1", "-n", str(number), "-M", "NCL", "-m", str(size), "-x", str(size)] try: passwords = subprocess.check_output(command).splitlines() except: # noqa: E722 print(_("error running apg")) passwords = None return passwords ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614242.0 pasaffe-0.57/pasaffe_lib/helpersgui.py0000644000175000017500000000335600000000000021510 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # """GUI helpers for an Ubuntu application.""" import os import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # noqa: E402 from . Builder import Builder # noqa: E402 from . pasaffeconfig import get_data_file # noqa: E402 def get_builder(builder_file_name): """Return a fully-instantiated Gtk.Builder instance from specified ui file :param builder_file_name: The name of the builder file, without extension. Assumed to be in the 'ui' directory under the data path. """ # Look for the ui file that describes the user interface. ui_filename = get_data_file('ui', '%s.ui' % (builder_file_name,)) if not os.path.exists(ui_filename): ui_filename = None builder = Builder() builder.set_translation_domain('pasaffe') builder.add_from_file(ui_filename) return builder def show_uri(parent, link): screen = parent.get_screen() try: Gtk.show_uri(screen, link, Gtk.get_current_event_time()) except: # noqa: E722 pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614632.0 pasaffe-0.57/pasaffe_lib/keepassx.py0000644000175000017500000002113400000000000021156 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011 Francesco Marella # Copyright (C) 2012 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import struct import os import time from binascii import hexlify from xml.etree import cElementTree as ET import logging logger = logging.getLogger('pasaffe_lib') class KeePassX: def __init__(self, filename=None): """ Reads a KeePassX/KeePass2 XML file""" self.records = {} self.skipped = [] self.index = 0 self.cipher = None self.parent_map = None if filename is not None: self.readfile(filename) def _convert_time(self, timestring, tz): """ Converts time format""" if timestring: if tz: value = time.mktime(time.strptime( timestring, "%Y-%m-%dT%H:%M:%SZ")) else: value = time.mktime(time.strptime( timestring, "%Y-%m-%dT%H:%M:%S")) else: value = time.time() return struct.pack(" # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # # THIS IS Pasaffe CONFIGURATION FILE # YOU CAN PUT THERE SOME GLOBAL VALUE # Do not touch unless you know what you're doing. # you're warned :) __all__ = ['project_path_not_found', 'get_data_file', 'get_data_path'] # Where your project will look for your data (for instance, images and ui # files). By default, this is ../data, relative your trunk layout __pasaffe_data_directory__ = '../data/' __license__ = 'GPL-3' __version__ = '0' __help_prefix__ = 'help:' __help_separator__ = '/' import os class project_path_not_found(Exception): """Raised when we can't find the project directory.""" def get_data_file(*path_segments): """Get the full path to a data file. Returns the path to a file underneath the data directory (as defined by `get_data_path`). Equivalent to os.path.join(get_data_path(), *path_segments). """ return os.path.join(get_data_path(), *path_segments) def get_data_path(): """Retrieve pasaffe data path This path is by default /../data/ in trunk and /usr/share/pasaffe in an installed version but this path is specified at installation time. """ # Get pathname absolute or relative. path = os.path.join( os.path.dirname(__file__), __pasaffe_data_directory__) abs_data_path = os.path.abspath(path) if not os.path.exists(abs_data_path): raise project_path_not_found return abs_data_path def get_version(): return __version__ def get_help_prefix(): return __help_prefix__ def get_help_separator(): return __help_separator__ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572614166.0 pasaffe-0.57/pasaffe_lib/pytwofish.py0000644000175000017500000004063600000000000021377 0ustar00mdeslaurmdeslaur00000000000000# twofish.py - pure Python implementation of the Twofish algorithm. # Bjorn Edstrom 13 december 2007. # # Copyrights # ========== # # This code is a derived from an implementation by Dr Brian Gladman # (gladman@seven77.demon.co.uk) which is subject to the following license. # This Python implementation is not subject to any other license. # # /* This is an independent implementation of the encryption algorithm: */ # /* */ # /* Twofish by Bruce Schneier and colleagues */ # /* */ # /* which is a candidate algorithm in the Advanced Encryption Standard */ # /* programme of the US National Institute of Standards and Technology. */ # /* */ # /* Copyright in this implementation is held by Dr B R Gladman but I */ # /* hereby give permission for its free direct or derivative use subject */ # /* to acknowledgment of its origin and compliance with any conditions */ # /* that the originators of t he algorithm place on its exploitation. */ # /* */ # /* My thanks to Doug Whiting and Niels Ferguson for comments that led */ # /* to improvements in this implementation. */ # /* */ # /* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999 */ # # The above copyright notice must not be removed. # # Information # =========== # # Anyone thinking of using this code should reconsider. It's slow. # Try python-mcrypt instead. In case a faster library is not installed # on the target system, this code can be used as a portable fallback. try: import psyco psyco.full() except ImportError: pass import struct import sys block_size = 16 key_size = 32 class Twofish: def __init__(self, key=None): """Twofish.""" if key: self.set_key(key) def set_key(self, key): """Init.""" key_len = len(key) if key_len not in [16, 24, 32]: # XXX: add padding? raise KeyError("key must be 16, 24 or 32 bytes") if key_len % 4: # XXX: add padding? raise KeyError("key not a multiple of 4") if key_len > 32: # XXX: prune? raise KeyError("key_len > 32") self.context = TWI() key_word32 = [0] * 32 i = 0 while key: key_word32[i] = struct.unpack("> n) | ((x << (32 - n)) & 0xFFFFFFFF) def rotl32(x, n): return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n)) def byteswap32(x): return ((x & 0xff) << 24) | (((x >> 8) & 0xff) << 16) | \ (((x >> 16) & 0xff) << 8) | ((x >> 24) & 0xff) class TWI: def __init__(self): self.k_len = 0 # word32 self.l_key = [0] * 40 # word32 self.s_key = [0] * 4 # word32 self.qt_gen = 0 # word32 self.q_tab = [[0] * 256, [0] * 256] # byte self.mt_gen = 0 # word32 self.m_tab = [[0] * 256, [0] * 256, [0] * 256, [0] * 256] # word32 self.mk_tab = [[0] * 256, [0] * 256, [0] * 256, [0] * 256] # word32 def byte(x, n): return (x >> (8 * n)) & 0xff tab_5b = [0, 90, 180, 238] tab_ef = [0, 238, 180, 90] ror4 = [0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15] ashx = [0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 5, 14, 7] qt0 = [[8, 1, 7, 13, 6, 15, 3, 2, 0, 11, 5, 9, 14, 12, 10, 4], [2, 8, 11, 13, 15, 7, 6, 14, 3, 1, 9, 4, 0, 10, 12, 5]] qt1 = [[14, 12, 11, 8, 1, 2, 3, 5, 15, 4, 10, 6, 7, 0, 9, 13], [1, 14, 2, 11, 4, 12, 3, 7, 6, 13, 10, 5, 15, 9, 0, 8]] qt2 = [[11, 10, 5, 14, 6, 13, 9, 0, 12, 8, 15, 3, 2, 4, 7, 1], [4, 12, 7, 5, 1, 6, 9, 10, 0, 14, 13, 8, 2, 11, 3, 15]] qt3 = [[13, 7, 15, 4, 1, 2, 6, 14, 9, 11, 3, 0, 8, 5, 12, 10], [11, 9, 5, 1, 12, 3, 13, 14, 6, 4, 7, 15, 2, 0, 8, 10]] def qp(n, x): # word32, byte n %= 0x100000000 x %= 0x100 a0 = x >> 4 b0 = x & 15 a1 = a0 ^ b0 b1 = ror4[b0] ^ ashx[a0] a2 = qt0[n][a1] b2 = qt1[n][b1] a3 = a2 ^ b2 b3 = ror4[b2] ^ ashx[a2] a4 = qt2[n][a3] b4 = qt3[n][b3] return (b4 << 4) | a4 def gen_qtab(pkey): for i in range(256): pkey.q_tab[0][i] = qp(0, i) pkey.q_tab[1][i] = qp(1, i) def gen_mtab(pkey): for i in range(256): f01 = pkey.q_tab[1][i] f01 = pkey.q_tab[1][i] f5b = ((f01) ^ ((f01) >> 2) ^ tab_5b[(f01) & 3]) fef = ((f01) ^ ((f01) >> 1) ^ ((f01) >> 2) ^ tab_ef[(f01) & 3]) pkey.m_tab[0][i] = f01 + (f5b << 8) + (fef << 16) + (fef << 24) pkey.m_tab[2][i] = f5b + (fef << 8) + (f01 << 16) + (fef << 24) f01 = pkey.q_tab[0][i] f5b = ((f01) ^ ((f01) >> 2) ^ tab_5b[(f01) & 3]) fef = ((f01) ^ ((f01) >> 1) ^ ((f01) >> 2) ^ tab_ef[(f01) & 3]) pkey.m_tab[1][i] = fef + (fef << 8) + (f5b << 16) + (f01 << 24) pkey.m_tab[3][i] = f5b + (f01 << 8) + (fef << 16) + (f5b << 24) def gen_mk_tab(pkey, key): if pkey.k_len == 2: for i in range(256): by = i % 0x100 pkey.mk_tab[0][i] = pkey.m_tab[0][pkey.q_tab[0][ pkey.q_tab[0][by] ^ byte(key[1], 0)] ^ byte(key[0], 0)] pkey.mk_tab[1][i] = pkey.m_tab[1][pkey.q_tab[0][ pkey.q_tab[1][by] ^ byte(key[1], 1)] ^ byte(key[0], 1)] pkey.mk_tab[2][i] = pkey.m_tab[2][pkey.q_tab[1][ pkey.q_tab[0][by] ^ byte(key[1], 2)] ^ byte(key[0], 2)] pkey.mk_tab[3][i] = pkey.m_tab[3][pkey.q_tab[1][ pkey.q_tab[1][by] ^ byte(key[1], 3)] ^ byte(key[0], 3)] if pkey.k_len == 3: for i in range(256): by = i % 0x100 pkey.mk_tab[0][i] = pkey.m_tab[0][pkey.q_tab[0][ pkey.q_tab[0][pkey.q_tab[1][by] ^ byte(key[2], 0)] ^ byte(key[1], 0)] ^ byte(key[0], 0)] pkey.mk_tab[1][i] = pkey.m_tab[1][pkey.q_tab[0][ pkey.q_tab[1][pkey.q_tab[1][by] ^ byte(key[2], 1)] ^ byte(key[1], 1)] ^ byte(key[0], 1)] pkey.mk_tab[2][i] = pkey.m_tab[2][pkey.q_tab[1][ pkey.q_tab[0][pkey.q_tab[0][by] ^ byte(key[2], 2)] ^ byte(key[1], 2)] ^ byte(key[0], 2)] pkey.mk_tab[3][i] = pkey.m_tab[3][pkey.q_tab[1][ pkey.q_tab[1][pkey.q_tab[0][by] ^ byte(key[2], 3)] ^ byte(key[1], 3)] ^ byte(key[0], 3)] if pkey.k_len == 4: for i in range(256): by = i % 0x100 pkey.mk_tab[0][i] = pkey.m_tab[0][pkey.q_tab[0][ pkey.q_tab[0][pkey.q_tab[1][ pkey.q_tab[1][by] ^ byte(key[3], 0)] ^ byte(key[2], 0)] ^ byte(key[1], 0)] ^ byte(key[0], 0)] pkey.mk_tab[1][i] = pkey.m_tab[1][pkey.q_tab[0][ pkey.q_tab[1][pkey.q_tab[1][ pkey.q_tab[0][by] ^ byte(key[3], 1)] ^ byte(key[2], 1)] ^ byte(key[1], 1)] ^ byte(key[0], 1)] pkey.mk_tab[2][i] = pkey.m_tab[2][pkey.q_tab[1][ pkey.q_tab[0][pkey.q_tab[0][ pkey.q_tab[0][by] ^ byte(key[3], 2)] ^ byte(key[2], 2)] ^ byte(key[1], 2)] ^ byte(key[0], 2)] pkey.mk_tab[3][i] = pkey.m_tab[3][pkey.q_tab[1][ pkey.q_tab[1][pkey.q_tab[0][ pkey.q_tab[1][by] ^ byte(key[3], 3)] ^ byte(key[2], 3)] ^ byte(key[1], 3)] ^ byte(key[0], 3)] def h_fun(pkey, x, key): b0 = byte(x, 0) b1 = byte(x, 1) b2 = byte(x, 2) b3 = byte(x, 3) if pkey.k_len >= 4: b0 = pkey.q_tab[1][b0] ^ byte(key[3], 0) b1 = pkey.q_tab[0][b1] ^ byte(key[3], 1) b2 = pkey.q_tab[0][b2] ^ byte(key[3], 2) b3 = pkey.q_tab[1][b3] ^ byte(key[3], 3) if pkey.k_len >= 3: b0 = pkey.q_tab[1][b0] ^ byte(key[2], 0) b1 = pkey.q_tab[1][b1] ^ byte(key[2], 1) b2 = pkey.q_tab[0][b2] ^ byte(key[2], 2) b3 = pkey.q_tab[0][b3] ^ byte(key[2], 3) if pkey.k_len >= 2: b0 = pkey.q_tab[0][pkey.q_tab[0][b0] ^ byte(key[1], 0)] ^ byte(key[0], 0) b1 = pkey.q_tab[0][pkey.q_tab[1][b1] ^ byte(key[1], 1)] ^ byte(key[0], 1) b2 = pkey.q_tab[1][pkey.q_tab[0][b2] ^ byte(key[1], 2)] ^ byte(key[0], 2) b3 = pkey.q_tab[1][pkey.q_tab[1][b3] ^ byte(key[1], 3)] ^ byte(key[0], 3) return pkey.m_tab[0][b0] ^ pkey.m_tab[1][b1] ^ \ pkey.m_tab[2][b2] ^ pkey.m_tab[3][b3] def mds_rem(p0, p1): i, t, u = 0, 0, 0 for i in range(8): t = p1 >> 24 p1 = ((p1 << 8) & 0xffffffff) | (p0 >> 24) p0 = (p0 << 8) & 0xffffffff u = (t << 1) & 0xffffffff if t & 0x80: u ^= 0x0000014d p1 ^= t ^ ((u << 16) & 0xffffffff) u ^= (t >> 1) if t & 0x01: u ^= 0x0000014d >> 1 p1 ^= ((u << 24) & 0xffffffff) | ((u << 8) & 0xffffffff) return p1 def set_key(pkey, in_key, key_len): pkey.qt_gen = 0 if not pkey.qt_gen: gen_qtab(pkey) pkey.qt_gen = 1 pkey.mt_gen = 0 if not pkey.mt_gen: gen_mtab(pkey) pkey.mt_gen = 1 pkey.k_len = int((key_len * 8) / 64) a = 0 b = 0 me_key = [0, 0, 0, 0] mo_key = [0, 0, 0, 0] for i in range(pkey.k_len): if WORD_BIGENDIAN: a = byteswap32(in_key[i + 1]) me_key[i] = a b = byteswap32(in_key[i + i + 1]) else: a = in_key[i + i] me_key[i] = a b = in_key[i + i + 1] mo_key[i] = b pkey.s_key[pkey.k_len - i - 1] = mds_rem(a, b) for i in range(0, 40, 2): a = (0x01010101 * i) % 0x100000000 b = (a + 0x01010101) % 0x100000000 a = h_fun(pkey, a, me_key) b = rotl32(h_fun(pkey, b, mo_key), 8) pkey.l_key[i] = (a + b) % 0x100000000 pkey.l_key[i + 1] = rotl32((a + 2 * b) % 0x100000000, 9) gen_mk_tab(pkey, pkey.s_key) def encrypt(pkey, in_blk): blk = [0, 0, 0, 0] if WORD_BIGENDIAN: blk[0] = byteswap32(in_blk[0]) ^ pkey.l_key[0] blk[1] = byteswap32(in_blk[1]) ^ pkey.l_key[1] blk[2] = byteswap32(in_blk[2]) ^ pkey.l_key[2] blk[3] = byteswap32(in_blk[3]) ^ pkey.l_key[3] else: blk[0] = in_blk[0] ^ pkey.l_key[0] blk[1] = in_blk[1] ^ pkey.l_key[1] blk[2] = in_blk[2] ^ pkey.l_key[2] blk[3] = in_blk[3] ^ pkey.l_key[3] for i in range(8): t1 = (pkey.mk_tab[0][byte(blk[1], 3)] ^ pkey.mk_tab[1][byte(blk[1], 0)] ^ pkey.mk_tab[2][byte(blk[1], 1)] ^ pkey.mk_tab[3][byte(blk[1], 2)]) t0 = (pkey.mk_tab[0][byte(blk[0], 0)] ^ pkey.mk_tab[1][byte(blk[0], 1)] ^ pkey.mk_tab[2][byte(blk[0], 2)] ^ pkey.mk_tab[3][byte(blk[0], 3)]) blk[2] = rotr32(blk[2] ^ ((t0 + t1 + pkey.l_key[4 * (i) + 8]) % 0x100000000), 1) blk[3] = rotl32(blk[3], 1) ^ \ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 9]) % 0x100000000) t1 = (pkey.mk_tab[0][byte(blk[3], 3)] ^ pkey.mk_tab[1][byte(blk[3], 0)] ^ pkey.mk_tab[2][byte(blk[3], 1)] ^ pkey.mk_tab[3][byte(blk[3], 2)]) t0 = (pkey.mk_tab[0][byte(blk[2], 0)] ^ pkey.mk_tab[1][byte(blk[2], 1)] ^ pkey.mk_tab[2][byte(blk[2], 2)] ^ pkey.mk_tab[3][byte(blk[2], 3)]) blk[0] = rotr32(blk[0] ^ ((t0 + t1 + pkey.l_key[4 * (i) + 10]) % 0x100000000), 1) blk[1] = rotl32(blk[1], 1) ^ \ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 11]) % 0x100000000) if WORD_BIGENDIAN: in_blk[0] = byteswap32(blk[2] ^ pkey.l_key[4]) in_blk[1] = byteswap32(blk[3] ^ pkey.l_key[5]) in_blk[2] = byteswap32(blk[0] ^ pkey.l_key[6]) in_blk[3] = byteswap32(blk[1] ^ pkey.l_key[7]) else: in_blk[0] = blk[2] ^ pkey.l_key[4] in_blk[1] = blk[3] ^ pkey.l_key[5] in_blk[2] = blk[0] ^ pkey.l_key[6] in_blk[3] = blk[1] ^ pkey.l_key[7] return def decrypt(pkey, in_blk): blk = [0, 0, 0, 0] if WORD_BIGENDIAN: blk[0] = byteswap32(in_blk[0]) ^ pkey.l_key[4] blk[1] = byteswap32(in_blk[1]) ^ pkey.l_key[5] blk[2] = byteswap32(in_blk[2]) ^ pkey.l_key[6] blk[3] = byteswap32(in_blk[3]) ^ pkey.l_key[7] else: blk[0] = in_blk[0] ^ pkey.l_key[4] blk[1] = in_blk[1] ^ pkey.l_key[5] blk[2] = in_blk[2] ^ pkey.l_key[6] blk[3] = in_blk[3] ^ pkey.l_key[7] for i in range(7, -1, -1): t1 = (pkey.mk_tab[0][byte(blk[1], 3)] ^ pkey.mk_tab[1][byte(blk[1], 0)] ^ pkey.mk_tab[2][byte(blk[1], 1)] ^ pkey.mk_tab[3][byte(blk[1], 2)]) t0 = (pkey.mk_tab[0][byte(blk[0], 0)] ^ pkey.mk_tab[1][byte(blk[0], 1)] ^ pkey.mk_tab[2][byte(blk[0], 2)] ^ pkey.mk_tab[3][byte(blk[0], 3)]) blk[2] = rotl32(blk[2], 1) ^ ((t0 + t1 + pkey.l_key[4 * (i) + 10]) % 0x100000000) blk[3] = rotr32(blk[3] ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 11]) % 0x100000000), 1) t1 = (pkey.mk_tab[0][byte(blk[3], 3)] ^ pkey.mk_tab[1][byte(blk[3], 0)] ^ pkey.mk_tab[2][byte(blk[3], 1)] ^ pkey.mk_tab[3][byte(blk[3], 2)]) t0 = (pkey.mk_tab[0][byte(blk[2], 0)] ^ pkey.mk_tab[1][byte(blk[2], 1)] ^ pkey.mk_tab[2][byte(blk[2], 2)] ^ pkey.mk_tab[3][byte(blk[2], 3)]) blk[0] = rotl32(blk[0], 1) ^ ((t0 + t1 + pkey.l_key[4 * (i) + 8]) % 0x100000000) blk[1] = rotr32(blk[1] ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 9]) % 0x100000000), 1) if WORD_BIGENDIAN: in_blk[0] = byteswap32(blk[2] ^ pkey.l_key[0]) in_blk[1] = byteswap32(blk[3] ^ pkey.l_key[1]) in_blk[2] = byteswap32(blk[0] ^ pkey.l_key[2]) in_blk[3] = byteswap32(blk[1] ^ pkey.l_key[3]) else: in_blk[0] = blk[2] ^ pkey.l_key[0] in_blk[1] = blk[3] ^ pkey.l_key[1] in_blk[2] = blk[0] ^ pkey.l_key[2] in_blk[3] = blk[1] ^ pkey.l_key[3] return __testkey = b'\xD4\x3B\xB7\x55\x6E\xA3\x2E\x46\xF2\xA2\x82\xB7\xD4' + \ b'\x5B\x4E\x0D\x57\xFF\x73\x9D\x4D\xC9\x2C\x1B\xD7\xFC' + \ b'\x01\x70\x0C\xC8\x21\x6F' __testdat = b'\x90\xAF\xE9\x1B\xB2\x88\x54\x4F\x2C\x32\xDC\x23\x9B\x26\x35\xE6' __testresult = b'l\xb4V\x1c@\xbf\n\x97\x05\x93\x1c\xb6\xd4\x08\xe7\xfa' assert Twofish(__testkey).encrypt(__testdat) == __testresult assert __testdat == Twofish(__testkey).decrypt(__testresult) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457819745.0 pasaffe-0.57/pasaffe_lib/pytwofishcbc.py0000644000175000017500000000302500000000000022036 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # from . import pytwofish import operator import struct class TwofishCBC(pytwofish.Twofish): cbc_iv = b"\0" * 16 def _xor(self, string, pw): result = b'' for k in range(len(string)): result += struct.pack("B", operator.xor(ord(string[k:k + 1]), ord(pw[k:k + 1]))) return result def initCBC(self, iv=b"\0" * 16): """Sets the CBC mode initialization vector.""" self.cbc_iv = iv def encryptCBC(self, block): """Encrypts using CBC mode""" self.cbc_iv = self.encrypt(self._xor(block, self.cbc_iv)) return self.cbc_iv def decryptCBC(self, block): decrypted_block = self._xor(self.decrypt(block), self.cbc_iv) self.cbc_iv = block return decrypted_block ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580230568.0 pasaffe-0.57/pasaffe_lib/readdb.py0000644000175000017500000007445000000000000020565 0ustar00mdeslaurmdeslaur00000000000000# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import struct import hashlib import hmac import random import re import os import time import tempfile import shutil if os.name == "posix": import pwd elif os.name == "nt": import getpass # import pwd from binascii import hexlify # noqa: E402 from unidecode import unidecode # noqa: E402 from pasaffe_lib.helpers import PathEntry # noqa: E402 from . import pytwofishcbc # noqa: E402 import logging # noqa: E402 logger = logging.getLogger('pasaffe_lib') from . pasaffeconfig import get_version # noqa: E402 class PassSafeFile: def __init__(self, filename=None, password=None, req_cipher='Twofish', fixup=True): '''Reads a Password Safe v3 file''' self.keys = {} self.header = {} self.records = {} self.cipher = None self.cipher_block_size = 0 self.hmac = None self.dbfile = None self.empty_folders = [] self.find_results = [] self.find_results_index = None self.find_value = "" # These fields need converting between strings and bytes self.header_text = [0x03, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x11] self.record_text = [0x02, 0x03, 0x04, 0x05, 0x06, 0x0d] # Use version 0x030B, since we support saving empty folders self.db_version = b'\x0B\x03' if req_cipher == 'Twofish': self.cipher = pytwofishcbc.TwofishCBC() self.cipher_block_size = self.cipher.get_block_size() else: raise ValueError("Sorry, we don't support %s yet." % req_cipher) if filename is not None: self.readfile(filename, password, fixup=fixup) def readfile(self, filename, password, fixup=True): '''Parses database file''' logger.debug('Opening database: %s' % filename) try: self.dbfile = open(filename, 'rb') except Exception: raise RuntimeError("Could not open %s. Aborting." % filename) tag = self.dbfile.read(4) if tag != b"PWS3": raise RuntimeError("File %s is not a password safe database." " Aborting." % filename) self._readkeys(password) self._readheader() self._readrecords() self._validatehmac() self.dbfile.close() self.dbfile = None if fixup: self._postread_fixup() # Now that we've read the file, but before we get rid of the # password, generate new keys for our next save self.new_keys(password) # Don't need the password anymore, clear it out password = '' def new_db(self, password): '''Creates a new database in memory''' self.keys['ITER'] = 2048 self.new_keys(password) self.header[0] = self.db_version self.header[1] = os.urandom(16) # uuid def check_password(self, password): '''Checks if password is valid''' stretched_key = self._keystretch(password, self.keys['SALT'], self.keys['ITER']) # Don't need the password anymore, clear it out password = '' if hashlib.sha256(stretched_key).digest() != self.keys['HP']: return False else: return True def new_keys(self, password): '''Generates new keys''' self.keys['SALT'] = os.urandom(32) stretched_key = self._keystretch(password, self.keys['SALT'], self.keys['ITER']) # Don't need the password anymore, clear it out password = '' self.keys['HP'] = hashlib.sha256(stretched_key).digest() self.cipher.set_key(stretched_key) # Don't need the stretched key anymore, clear it out stretched_key = '' b1_rand = os.urandom(16) b2_rand = os.urandom(16) b3_rand = os.urandom(16) b4_rand = os.urandom(16) self.keys['K'] = b1_rand + b2_rand self.keys['L'] = b3_rand + b4_rand self.keys['B1'] = self.cipher.encrypt(b1_rand) self.keys['B2'] = self.cipher.encrypt(b2_rand) self.keys['B3'] = self.cipher.encrypt(b3_rand) self.keys['B4'] = self.cipher.encrypt(b4_rand) self.keys['IV'] = os.urandom(16) def writefile(self, filename, backup=False): '''Writes database file''' # Set username if os.name == "posix": self.header[7] = pwd.getpwuid(os.getuid())[0] elif os.name == "nt": self.header[7] = getpass.getuser() # Remove the old deprecated username field if it exists if 5 in self.header: del self.header[5] # Set hostname self.header[8] = os.uname()[1] # Set timestamp self.header[4] = struct.pack(" 100000: raise RuntimeError("Too many iterations: %s. Aborting." % self.keys['ITER']) logger.debug("Number of iters is %d" % self.keys['ITER']) self.keys['HP'] = self.dbfile.read(32) # logger.debug("hp is %s" % self.keys['HP']) self.keys['B1'] = self.dbfile.read(16) self.keys['B2'] = self.dbfile.read(16) self.keys['B3'] = self.dbfile.read(16) self.keys['B4'] = self.dbfile.read(16) self.keys['IV'] = self.dbfile.read(16) self.cipher.initCBC(self.keys['IV']) stretched_key = self._keystretch(password, self.keys['SALT'], self.keys['ITER']) # Don't need the password anymore, clear it out password = '' # logger.debug("stretched pass is %s" % hexlify(stretched_key)) if hashlib.sha256(stretched_key).digest() != self.keys['HP']: raise ValueError("Password supplied doesn't match database." " Aborting.") self.cipher.set_key(stretched_key) # Don't need the stretched key anymore, clear it out stretched_key = b'' self.keys['K'] = self.cipher.decrypt(self.keys['B1']) + \ self.cipher.decrypt(self.keys['B2']) self.keys['L'] = self.cipher.decrypt(self.keys['B3']) + \ self.cipher.decrypt(self.keys['B4']) self.hmac = hmac.new(self.keys['L'], digestmod=hashlib.sha256) # logger.debug("K is %s and L is %s" % (hexlify(self.keys['K']), # hexlify(self.keys['L'])) def _writekeys(self): self.dbfile.write(self.keys['SALT']) self.dbfile.write(struct.pack("i", self.keys['ITER'])) self.dbfile.write(self.keys['HP']) self.dbfile.write(self.keys['B1']) self.dbfile.write(self.keys['B2']) self.dbfile.write(self.keys['B3']) self.dbfile.write(self.keys['B4']) self.dbfile.write(self.keys['IV']) self.cipher.initCBC(self.keys['IV']) self.hmac = hmac.new(self.keys['L'], digestmod=hashlib.sha256) def _readheader(self): self.cipher.set_key(self.keys['K']) while(1): status, field_type, field_data = self._readfield() if status is False: raise RuntimeError("Malformed file, " "was expecting more data in header") # Convert from bytes to strings if field_type in self.header_text: field_data = field_data.decode('utf-8') if field_type == 0xff: logger.debug("Found end field") break elif field_type == 0x11: # Empty group fields can appear more than once # Store them in their own variable self.empty_folders.append(field_data) logger.debug("found empty folder: %s" % field_data) else: self.header[field_type] = field_data logger.debug("Found field 0x%.2x" % field_type) def _writeheader(self): self.cipher.set_key(self.keys['K']) # v3.30 of the spec says the version type needs to be the # first field in the header. Handle it first. logger.debug("Writing Version Type field") self._writefield(0x00, self.header[0x00]) for entry in list(self.header.keys()): # Skip Version Type, we've already handled it if entry == 0x00: continue # Convert from strings to bytes if entry in self.header_text: value = self.header[entry].encode('utf-8') else: value = self.header[entry] logger.debug("Writing %.2x" % entry) self._writefield(entry, value) # Now handle empty folders logger.debug("Writing empty folders") for folder in self.empty_folders: logger.debug("writing empty folder: %s" % folder) self._writefield(0x11, folder.encode('utf-8')) self._writefieldend() def _readrecords(self): self.cipher.set_key(self.keys['K']) record = {} while(1): status, field_type, field_data = self._readfield() if status is False: break if field_type == 0xff: logger.debug("Found end field") uuid = hexlify(record[1]).decode('utf-8') self.records[uuid] = record record = {} else: # Convert from bytes to strings if field_type in self.record_text: field_data = field_data.decode('utf-8') record[field_type] = field_data logger.debug("Found field 0x%.2x" % field_type) def _writerecords(self): self.cipher.set_key(self.keys['K']) for uuid in self.records: for field in list(self.records[uuid].keys()): # Fix up some of the fields fixed_value = self._presave_fixup(uuid, field) # Convert from strings to bytes if field in self.record_text: value = fixed_value.encode('utf-8') else: value = fixed_value self._writefield(field, value) self._writefieldend() def _readfield(self): field_data = b'' status, first_block = self._readblock() if status is False: return False, 0xFF, b'' field_length = struct.unpack(" 0: logger.debug("extra block") status, data = self._readblock() if status is False: raise RuntimeError("Malformed file, " "was expecting more data") field_data += data[0:field_length] field_length -= self.cipher_block_size logger.debug("actual data length is %d" % len(field_data)) self.hmac.update(field_data) return True, field_type, field_data def _writefield(self, field_type, field_data): self.hmac.update(field_data) field_length = len(field_data) field_free_space = self.cipher_block_size - 5 index = 0 block = b'' block += struct.pack("I", field_length) block += struct.pack("B", field_type) logger.debug("Writing field type %.2x, length %d" % (field_type, field_length)) while field_length >= 0: if field_length < field_free_space: logger.debug("smaller than block") block += field_data[index:index + field_length] for x in range(field_free_space - field_length): block += struct.pack("B", random.randint(0, 254)) self._writeblock(block) field_length = -1 block = b'' else: logger.debug("bigger than block") block += field_data[index:index + field_free_space] self._writeblock(block) field_length -= field_free_space if field_length == 0: field_length = -1 index += field_free_space field_free_space = self.cipher_block_size block = b'' def _writefieldend(self): block = struct.pack("I", 0) block += struct.pack("B", 0xff) logger.debug("Writing field end") for x in range(self.cipher_block_size - 5): block += struct.pack("B", random.randint(0, 254)) self._writeblock(block) def _readblock(self): block = self.dbfile.read(self.cipher_block_size) if block == b'PWS3-EOFPWS3-EOF': return False, block return True, self.cipher.decryptCBC(block) def _writeblock(self, block): logger.debug("writing block, length is %d" % len(block)) self.dbfile.write(self.cipher.encryptCBC(block)) def _writeeofblock(self): self.dbfile.write(b'PWS3-EOFPWS3-EOF') def _validatehmac(self): hmac = self.dbfile.read(32) if hmac != self.hmac.digest(): raise RuntimeError("Malformed file, HMAC didn't match!") else: logger.debug("HMAC Matched") self.hmac = None def _writehmac(self): self.dbfile.write(self.hmac.digest()) self.hmac = None def _postread_fixup(self): '''Performs some cleanup after reading certain databases''' for uuid in self.records: # Some apps don't create username fields, so let's default # to what the real Password Safe does and fix it up to be a # field with an empty string if 4 not in self.records[uuid]: self.records[uuid][4] = '' # Do basically the same with password fields if 6 not in self.records[uuid]: self.records[uuid][6] = '' # And the same with title fields if 3 not in self.records[uuid]: self.records[uuid][3] = '' # Most apps use CRLF line terminators. Convert them to LF # when opening in Pasaffe, we'll convert them back to CRLF # before saving if 5 in self.records[uuid]: self.records[uuid][5] = self.records[uuid][5].replace( "\r\n", "\n") def _presave_fixup(self, uuid, field): '''Performs some cleanup before saving certain databases''' if field == 5: # Most apps use CRLF line terminators. Convert LF to CRLF # before saving return self.records[uuid][5].replace("\n", "\r\n") else: return self.records[uuid][field] def update_find_results(self, find, force=False): if find == "": self.find_results = [] self.find_results_index = None self.find_value = "" return if find == self.find_value and force is False: return find_ascii = unidecode(find) if find_ascii != find: pat = re.compile(find + "|" + find_ascii, re.IGNORECASE) else: pat = re.compile(find, re.IGNORECASE) record_list = (3, 5, 13) results = [] for uuid in self.records: found = False for record_type in record_list: if record_type in self.records[uuid]: if pat.search( self.records[uuid].get(record_type)): found = True break if pat.search(unidecode( self.records[uuid].get(record_type))): found = True break if found is True: entry = PathEntry( self.records[uuid][3], uuid, self.get_folder_list(uuid)) results.append(entry) self.find_results = sorted(results) self.find_results_index = None self.find_value = find def get_next_find_result(self, backwards=False): if len(self.find_results) == 0: return None if self.find_results_index is None: self.find_results_index = 0 elif backwards is False: self.find_results_index += 1 if self.find_results_index == len(self.find_results): self.find_results_index = 0 else: if self.find_results_index == 0: self.find_results_index = len(self.find_results) - 1 else: self.find_results_index -= 1 return self.find_results[self.find_results_index].uuid ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/po/0000775000175000017500000000000000000000000015145 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/de.po0000644000175000017500000004543700000000000016110 0ustar00mdeslaurmdeslaur00000000000000# German translation for pasaffe # Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 # This file is distributed under the same license as the pasaffe package. # FIRST AUTHOR , 2012. # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2019-09-25 07:00+0000\n" "Last-Translator: Marc Deslauriers \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-29 05:52+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "A_bbrechen" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "_OK" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "Ordner bearbeiten" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "Übergeordneter Ordner:" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "Ordnername:" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "Der übergeordnete Ordner für diesen neuen Ordner" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "Willkommen bei Pasaffe!" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" "Keine existierende Datenbank wurde gefunden.\n" "Eine neue wird erstellt.\n" "Bitte geben Sie ein Hauptpasswort ein, um Sie zu schützen." #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "Passwörter passen nicht! Bitte erneut versuchen." #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "Passwort eingeben:" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "Passwort bestätigen:" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "Bitte geben Sie ihr Hauptpasswort ein:" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "Ungültiges Passwort! Bitte erneut versuchen." #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "Schließen, ohne zu speichern" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "_Speichern" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" "Änderungen in der Datenbank vorm Schließen speichern?" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "Ihre Änderungen gehen verloren, wenn Sie sie nicht speichern." #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "Hauptpasswort ändern" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" "Geben Sie ihr altes Hauptpasswort ein,\n" "und ein neues Hauptpasswort.\n" "Sie müssen das neue Passwort bestätigen." #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "Fehlermeldung" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "Altes Passwort:" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "Neues Passwort:" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "Neues Passwort bestätigen:" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "http://www.launchpad.net/pasaffe" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " Daniel Winzen https://launchpad.net/~q-d-deactivatedaccount\n" " Dennis Baudys https://launchpad.net/~thecondordb\n" " Marc Deslauriers https://launchpad.net/~mdeslaur\n" " tschoie https://launchpad.net/~tschoie" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "" "Meldungen zur Fehlerdiagnose anzeigen (-vv diagnostiziert auch pasaffe_lib)" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "Datenbank DATEI verwenden" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "Datenbank als Vorgabe festlegen" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "Eintrag bearbeiten" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "Der Name für diesen Eintrag" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "Der Ordner für diesen Eintrag" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "Benutzername zu diesem Eintrag" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "Titel:" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "Ordner:" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "Benutzername:" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "Passwort:" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "Notizen:" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "URL:" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "Die Webseite für diesen Eintrag" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "Wählen Sie ein beliebiges Passwort" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "Pasaffe" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "_Datei" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "Sperren" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "Hauptpasswort ändern" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "S_chließen" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "_Bearbeiten" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "Eintrag hinzufügen" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "Ordner hinzufügen" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "Duplizieren" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "_Löschen" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "Finden" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "URL kopieren" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "Benutzername kopieren" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "Passwort kopieren" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "_Einstellungen" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "_Ansicht" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "Vertrauliches anzeigen" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "URL öffnen" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "Datenbank Information" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "_Hilfe" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "Inhalt" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "_Info" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "Änderungen speichern" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "Speichern" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "Einen neuen Eintrag hinzufügen" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "Hinzufügen" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "Einen neuen Ordner hinzufügen" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "Den ausgewählten Eintrag bearbeiten" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "Bearbeiten" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "Den ausgewählten Eintrag löschen" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "Entfernen" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "Die URL öffnen" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "Den Benutzernamen kopieren" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "Das Passwort kopieren" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "Einträge finden" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "Vertrauliches anzeigen" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "Symbol" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "Name" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "Suche schließen" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "Vorheriges Ergebnis" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "Nächstes Ergebnis" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "Die Passwortdatenbank ist derzeit gesperrt." #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "_Beenden" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "Entsperren" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Pasaffe Passwort-Verwalter" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "Vertrauliches ist derzeit versteckt." #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "Zuletzt aktualisiert:" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "Passwort aktualisiert:" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Wilkommen bei Pasaffe!" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" "Pasaffe ist ein einfach zu benutzender\n" "Passwortverwalter." #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "Dies ist ein Ordner." #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "Neuer Ordner" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" "Sind Sie sicher, dass Sie \"%s\" entfernen möchten?\n" "\n" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "Der Inhalt des Eintrags wird verloren gehen.\n" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" "Soll der Ordner \"%s\" wirklich gelöscht werden?\n" "\n" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "Alle Einträge in diesem Ordner werden verloren gehen.\n" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" "Datenbank Information\n" "\n" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "Zahl der Einträge: %s\n" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "Zuletzt gespeichert von: %s\n" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "Zuletzt gespeichert auf dem Rechner: %s\n" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "Letztes Speicherdatum: %s\n" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "Datenbankversion: %s\n" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "Anwendung benutzt: %s\n" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" "Ort der Datenbank:\n" "%s\n" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "Neues Passwort kann nicht leer sein! Bitte erneut versuchen." #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "Altes Passwort ist ungültig! Bitte erneut versuchen." #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "ausgeklappt beginnen" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "eingeklappt beginnen" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "werden vom letzten Speicherzeitpunkt erinnert" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "Pasaffe Einstellungen" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "Automatisch Änderungen speichern" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "Vertrauliches als Vorgabe anzeigen" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "Nur Passwörter sind vertraulich" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "Vertrauliches" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "Datenbank sperren nach" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "Minuten" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "Sperren" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "Passwörter sind" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "Zeichen lang" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "Passworterzeugung" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "Ordner" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "Ordnereinstellungen" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/en_GB.po0000644000175000017500000004411500000000000016462 0ustar00mdeslaurmdeslaur00000000000000# English (United Kingdom) translation for pasaffe # Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 # This file is distributed under the same license as the pasaffe package. # FIRST AUTHOR , 2013. # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2019-09-25 07:00+0000\n" "Last-Translator: Marc Deslauriers \n" "Language-Team: English (United Kingdom) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-29 05:52+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "_Cancel" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "_OK" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "Edit folder" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "Parent folder:" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "Folder name:" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "The parent folder for this new folder" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "Welcome to Pasaffe!" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "Passwords don't match! Please try again." #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "Enter password:" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "Confirm password:" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "Please enter your master password:" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "Invalid password! Please try again." #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "Close without Saving" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "_Save" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" "Save changes to your password database before closing?" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "Your changes will be lost if you don't save them." #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "Change master password" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "error message" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "Old password:" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "New password:" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "Confirm new password:" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "http://www.launchpad.net/pasaffe" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " Anthony Harrington https://launchpad.net/~linuxchemist\n" " Marc Deslauriers https://launchpad.net/~mdeslaur" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "Show debug messages (-vv debugs pasaffe_lib also)" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "use database FILE" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "set database as default" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "Edit entry" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "The name for this entry" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "The folder for this entry" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "The entry's username" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "Title:" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "Folder:" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "Username:" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "Password:" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "Notes:" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "URL:" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "The website for this entry" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "Pick a random password" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "Pasaffe" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "_File" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "Lock" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "Change Master Password" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "_Close" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "_Edit" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "Add Entry" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "Add Folder" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "Clone" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "_Delete" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "Find" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "Copy URL" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "Copy Username" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "Copy Password" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "_Preferences" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "_View" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "Display Secrets" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "Open URL" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "Database Information" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "_Help" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "Contents" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "_About" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "Save changes" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "Save" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "Add a new entry" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "Add" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "Add a new folder" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "Edit the selected entry" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "Edit" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "Delete the selected entry" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "Remove" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "Open the URL" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "Copy the username" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "Copy the password" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "Find entries" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "Display secrets" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "icon" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "name" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "Close find" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "Previous result" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "Next result" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "The password database is currently locked." #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "_Quit" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "Unlock" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Pasaffe password manager" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "Secrets are currently hidden." #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "Last updated:" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "Password updated:" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Welcome to Pasaffe!" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" "Pasaffe is an easy to use\n" "password manager." #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "This is a folder." #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "New Folder" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" "Are you sure you wish to remove \"%s\"?\n" "\n" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "Contents of the entry will be lost.\n" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "All entries in this folder will be lost.\n" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" "Database Information\n" "\n" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "Number of entries: %s\n" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "Last saved by: %s\n" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "Last saved on host: %s\n" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "Last save date: %s\n" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "Database version: %s\n" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "Application used: %s\n" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" "Database location:\n" "%s\n" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "New password cannot be blank! Please try again." #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "Old password is invalid! Please try again." #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "start expanded" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "start collapsed" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "are remembered from last save" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "Pasaffe Preferences" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "Automatically save changes" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "Display secrets by default" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "Only passwords are secret" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "Secrets" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "Lock database after" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "minutes" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "Lock" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "Passwords are" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "characters long" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "Password Generator" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "Folders" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "Folder Preferences" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/es.po0000644000175000017500000004520600000000000016121 0ustar00mdeslaurmdeslaur00000000000000# Spanish translation for pasaffe # Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 # This file is distributed under the same license as the pasaffe package. # FIRST AUTHOR , 2012. # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2016-03-30 22:23+0000\n" "Last-Translator: Adolfo Jayme \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-29 05:52+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "Editar la carpeta" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "Carpeta contenedora:" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "Nombre de la carpeta:" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "La carpeta que contiene esta carpeta nueva" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "Bienvenido/a a Pasaffe." #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" "No se encontró ninguna base de datos.\n" "Por lo tanto, se creará una nueva.\n" "Introduzca una contraseña maestra para protegerla." #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "Las contraseñas no coinciden, inténtelo de nuevo." #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "Introduzca la contraseña:" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "Confirmar contraseña:" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "Introduzca su contraseña maestra:" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "La contraseña no es válida, inténtelo de nuevo." #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "Cerrar sin guardar" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" "¿Guardar los cambios a su base de datos de contraseñas antes de " "cerrar?" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "Perderá los cambios si no los guarda." #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "Cambiar contraseña maestra" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" "Introduzca su contraseña maestra anterior,\n" "y posteriormente una contraseña nueva.\n" "Debe confirmar la contraseña nueva." #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "mensaje de error" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "Contraseña anterior:" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "Contraseña nueva:" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "Confirme la contraseña nueva:" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "http://www.launchpad.net/pasaffe" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " Adolfo Jayme https://launchpad.net/~fitojb\n" " Marc Deslauriers https://launchpad.net/~mdeslaur" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "Mostrar mensajes de depuración (-vv depura pasaffe_lib también)" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "usar ARCHIVO de base de datos" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "establecer base de datos como predeterminada" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "Editar entrada" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "El nombre de esta entrada" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "La carpeta para esta entrada" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "El nombre de usuario de esta entrada" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "Título:" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "Carpeta:" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "Nombre de usuario:" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "Contraseña:" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "Notas:" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "URL:" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "El sitio web de esta entrada" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "Elija una contraseña aleatoria" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "crear una base de datos de Pasaffe nueva" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "URL de esta entrada" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "Pasaffe" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "_Archivo" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "Bloquear" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "Cambiar contraseña maestra" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "_Cerrar" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "_Editar" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "Añadir entrada" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "Añadir carpeta" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "Clonar" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "Buscar" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "Copiar URL" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "Copiar nombre de usuario" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "Copiar contraseña" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "_Ver" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "Mostrar secretos" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "Abrir URL" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "Información de la base de datos" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "Ay_uda" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "Contenidos" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "Guardar cambios" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "Guardar" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "Añadir una entrada nueva" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "Añadir" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "Añadir una carpeta nueva" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "Editar la entrada seleccionada" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "Editar" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "Eliminar la entrada seleccionada" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "Eliminar" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "Abrir la URL" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "Copiar el nombre de usuario" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "Copiar la contraseña" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "Buscar entradas" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "Mostrar secretos" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "icono" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "nombre" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "Cerrar" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "Resultado anterior" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "Resultado siguiente" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "" "La base de datos de contraseñas está bloqueada actualmente." #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "Desbloquear" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "error al ejecutar apg" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Gestor de contraseñas Pasaffe" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "Los secretos están ocultos actualmente." #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "Última actualización:" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "Contraseña actualizada:" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Bienvenido/a a Passaffe." #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" "Pasaffe es un gestor de contraseñas\n" "fácil de usar." #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "Esto es una carpeta." #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "Nueva Carpeta" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" "¿Está seguro de que quiere quitar «%s»?\n" "\n" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "Se perderá todo el contenido de la entrada.\n" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" "¿Está seguro de que quiere eliminar la carpeta «%s»?\n" "\n" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "Se perderán todas las entradas en esta carpeta.\n" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" "Información de la base de datos\n" "\n" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "Número de entradas: %s\n" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "Guardado por última vez por: %s\n" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "Guardado por última vez en el equipo: %s\n" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "Guardado por última vez el: %s\n" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "Versión de la base de datos: %s\n" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "Aplicación usada: %s\n" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "La contraseña nueva no puede estar vacía, inténtelo de nuevo." #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "La contraseña anterior no es válida, inténtelo de nuevo." #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "Preferencias de Pasaffe" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "Guardar automáticamente los cambios" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "Mostrar secretos de manera predeterminada" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "Solo las contraseñas son secretas" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "Secretos" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "Bloquear la base de datos después de" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "minutos" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "Bloquear" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "Las contraseñas son de" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "caracteres" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "Generador de contraseñas" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "Carpetas" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "Preferencias de carpetas" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/fr.po0000644000175000017500000005160300000000000016117 0ustar00mdeslaurmdeslaur00000000000000# French translation for pasaffe # Copyright (C) 2011 Marc Deslauriers # This file is distributed under the same license as the pasaffe package. # Marc Deslauriers , 2011 # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2020-05-06 10:43+0000\n" "Last-Translator: Jean-Marc \n" "Language-Team: French \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-05-07 04:44+0000\n" "X-Generator: Launchpad (build fbdff7602bd10fb883bf7e2ddcc7fd5a16f60398)\n" "Language: fr\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "_Annuler" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "_Valider" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "Modifier le dossier" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "Dossier parent :" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "Nom du dossier :" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "Le dossier parent pour ce nouveau dossier" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "Bienvenue sur Pasaffe !" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" "Aucune base de données existante n'a été trouvée.\n" "Une nouvelle sera créé.\n" "Veuillez entrer un mot de passe principal pour la protéger." #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "Les mots de passe sont différents ! Veuillez recommencer." #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "Entrer le mot de passe :" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "Confirmer le mot de passe :" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "Veuillez entrer votre mot de passe principal :" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "Mot de passe invalide ! Veuillez essayer de nouveau." #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "Fermer sans sauvegarder" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "_Sauvegarder" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" "Sauvegarder les modifications à votre base de données avant de " "fermer ?" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "Vos modifications seront perdues si vous ne les enregistrez pas." #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "Changement de mot de passe principal" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" "Entrez votre ancien mot de passe principal,\n" "et un nouveau mot de passe principal.\n" "Vous devez confirmer le nouveau mot de passe." #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "message d'erreur" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "Ancien mot de passe :" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "Nouveau mot de passe :" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "Confirmer le nouveau mot de passe :" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "http://www.launchpad.net/pasaffe" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Marc Deslauriers \n" "\n" "Launchpad Contributions:\n" " Binnette https://launchpad.net/~binnette\n" " Cedric Puchalver https://launchpad.net/~cpuchalver\n" " Eliovir https://launchpad.net/~eliovir\n" " Jean-Marc https://launchpad.net/~m-balthazar\n" " LEROY Jean-Christophe https://launchpad.net/~celtic2-deactivatedaccount\n" " Marc Deslauriers https://launchpad.net/~mdeslaur\n" " londumas https://launchpad.net/~helion331990" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "" "Afficher les messages de déboguage (-vv les affiche également pour " "pasaffe_lib)" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "utiliser la base de données FILE" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "définir la base de données par défaut" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "Modifier une entrée" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "Le nom de l'entrée" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "Le dossier de cette entrée" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "Nom de l'utilisateur pour l'entrée" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "Titre :" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "Dossier :" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "Nom d'utilisateur :" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "Mot de passe :" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "Notes :" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "URL :" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "Le site web pour cette entrée" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "Sélectionner un mot de passe aléatoire" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "lance avec déboguage" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "spécifie une base de donnée GPass alternative" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "Ajoute une nouvelle entrée à la base de donnée" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "remplace une entrée dans la base de donnée" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "supprime une entrée de la base de donnée" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" "affiche une entrée complète dans la base de donnée, excepté le mot de passe" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" "cherche pour --entry en tant que composante d'un mot (défault : False)" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "affiche le champ group de l'entrée donnée" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "affiche le champ utilisateur de l'entrée donnée" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "affiche le champ notes de l'entrée donnée" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "affiche le champ mot de passe de l'entrée donnée" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "affiche le champ URL de l'entrée donnée" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "créé une nouvelle base de donnée Pasaffe" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "stipule le mot de passe maître de la base de donnée Pasaffe" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "stipule le nouveau mot de passe maître de la base de donnée Pasaffe" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "agit sur cette entrée de la base de donnée" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "groupe pour cette entrée" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "utilisateur pour cette entrée" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "mot de passe / phrase de chiffrement pour cette entrée" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "URL pour cette entrée" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "notes en format libre pour cette entrée" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" "lors de l'ajout d'une entrée, au moins le champ utilisateur doit être fournit" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" "--repl DOIT être utilisé avec au moins un des --newentry, --group, --user, --" "pswd, --url, ou --notes" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" "ERREUR : seulement un des --createdb, --list, --add, --repl, ou --del peut " "être spécifié" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "ne peut pas utiliser --list* avec un des --add, --del, ou --repl" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "un des --entry, --createdb, ou --genpswd DOIT être fournit" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" "ERREUR : --createdb demandé, mais il y a déjà un ficher avec le même nom" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "Mot de passe> " #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "Pasaffe" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "_Fichier" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "Verrouiller" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "Modifier le mot de passe principal" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "_Fermer" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "_Éditer" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "Ajouter une entrée" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "Ajouter le dossier" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "Dupliquer" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "_Supprimer" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "Rechercher" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "Copier l'URL" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "Copier le nom d'utilisateur" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "Copier le mot de passe" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "_Préférences" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "_Visualiser" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "Afficher les secrets" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "Ouvrir l'URL" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "Informations sur la base de données" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "_Aide" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "Contenu" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "_À propos" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "Enregistrer les modifications" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "Enregistrer" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "Ajouter une nouvelle entrée" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "Ajouter" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "Ajouter un nouveau dossier" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "Éditer l'entrée sélectionnée" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "Éditer" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "Supprimer l'entrée sélectionnée" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "Supprimer" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "Ouvrir l'URL" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "Copier le nom d'utilisateur" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "Copier le mot de passe" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "Rechercher" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "Afficher les secrets" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "icône" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "nom" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "Fermer" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "Résultat précédent" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "Résultat suivant" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "La base de données est verrouillée." #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "_Quitter" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "Déverrouiller" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Gestionnaire de mot de passe Pasaffe" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "[Sans titre]" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "Les secrets sont cachés." #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "Dernière mise à jour :" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "Mot de passe changé :" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Bienvenue à Pasaffe !" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" "Pasaffe est un gestionnaire de mots de\n" "passe facile à utiliser." #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "Ceci est un dossier." #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "Nouveau dossier" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" "Êtes-vous certain de vouloir supprimer « %s » ?\n" "\n" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "Le contenu de l'entrée sera perdu.\n" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" "Êtes-vous sûr de vouloir supprimer le dossier « %s » ?\n" "\n" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "Toutes les entrées dans ce dossier seront perdues.\n" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "Nouvelle entrée" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "Nouveau dossier" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" "Informations sur la base de données\n" "\n" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "Nombre d'entrées : %s\n" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "Sauvegardé par : %s\n" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "Sauvegardé sur : %s\n" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "Date de la sauvegarde : %s\n" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "Version de la base de données : %s\n" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "Application utilisée : %s\n" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" "Emplacement de la base de données :\n" "%s\n" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "Le mot de passe ne peut être vide ! Veuillez recommencer." #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "Ancien mot de passe invalide ! Veuillez recommencer." #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "sont ouverts au démarrage" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "sont fermés au démarrage" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "sont dans l'état de la dernière sauvegarde" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "modifie une entrée" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "copie le mot de passe" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "Préférences de Pasaffe" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "Enregistrer automatiquement les modifications" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "Afficher les secrets par défaut" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "Seuls les mot de passes sont secrets" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "Secrets" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "Verrouiller la base de données après" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "minutes" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "Verrouiller" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "Les mots de passe ont" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "caractères" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "Générateur de mot de passe" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "Dossiers" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "Un double-clic" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "Afficher les noms d'utilisateur" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "Options des dossiers" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/it.po0000644000175000017500000004352200000000000016125 0ustar00mdeslaurmdeslaur00000000000000# Italian translation for pasaffe # Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 # This file is distributed under the same license as the pasaffe package. # FIRST AUTHOR , 2011. # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2014-02-11 22:22+0000\n" "Last-Translator: Marc Deslauriers \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-29 05:52+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "Benvenuti in Pasaffe!" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" "Nessun database è stato trovato.\n" "Ne verrà quindi creato uno nuovo.\n" "Per favore inserisci la password principale per proteggerlo." #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "Le password non coincidono! Per favore riprova." #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "Inserisci password:" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "Conferma password:" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "Inserisci la tua password principale:" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "Password non valida! Riprova." #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" "Salvare i cambiamenti nel database prima di chiudere?" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "Le modifiche verranno perse se non saranno salvate." #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "Cambiare password principale" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" "Inserisci la tua vecchia password principale,\n" "e quella nuova.\n" "Devi confermare la nuova password." #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "messaggio di errore" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "Vecchia password:" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "Nuova password:" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "Conferma nuova password:" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "http://www.launchpad.net/pasaffe" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " Francesco Marella https://launchpad.net/~francesco-marella\n" " Marc Deslauriers https://launchpad.net/~mdeslaur\n" " Paolo Stivanin https://launchpad.net/~polslinux\n" " paolo https://launchpad.net/~c-admin-polslinux-it" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "Mostra messaggi debug (-vv debugs pasaffe_lib also)" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "Usare il database FILE" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "Impostare il database come predefinito" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "Modifica elemento" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "Il nome per questo elemento" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "L'username dell'elemento" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "Titolo:" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "Nome utente:" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "Password:" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "Note:" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "URL:" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "Il sito internet per questo elemento" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "Scegliere una password casuale" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "Pasaffe" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "_File" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "Blocca" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "Cambia Password Principale" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "_Modifica" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "Clona" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "Cercare" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "Copia URL" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "Copia Username" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "Copia Password" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "_Visualizza" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "Visualizza Segreti" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "Apri URL" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "Informazione sul database" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "_Aiuto" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "Contenuti" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "Salva modifiche" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "Salva" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "Aggiungi una nuova voce" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "Aggiungi" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "Modifica la voce selezionata" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "Modifica" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "Elimina la voce selezionata" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "Apri l'URL" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "Copia l'username" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "Copia la password" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "Cercare le voci" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "Visualizza segreti" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "Chiudere la ricerca" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "Risultato precedente" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "Risultato successivo" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "Il database è attualmente bloccato." #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "Sbloccare" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Pasaffe password manager" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "Attualmente i segreti sono nascosti." #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "Ultimo aggiornamento:" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "Password aggiornate:" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Benvenuti in Pasaffe!" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" "Pasaffe è un password manager\n" "semplice da usare" #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "" #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" "Sei sicuro di voler rimuovere \"%s\"?\n" "\n" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "Il contenuto della voce verrà perso.\n" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" "Informazioni database\n" "\n" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "Numero di elementi: %s\n" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "Ultimo salvataggio effettuato da: %s\n" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "Ultimo salvataggio sull'host: %s\n" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "Ultima data di salvataggio: %s\n" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "Applicazione usate: %s\n" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "La password non può essere vuota! Per favore riprova." #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "La vecchia password non è valida! Per favore riprova." #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "Preferenze Pasaffe" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "Salva cambiamenti automaticamente" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "Visualizza segreti di default" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "Solo le passwords sono segrete" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/ko.po0000644000175000017500000004013500000000000016117 0ustar00mdeslaurmdeslaur00000000000000# Korean translation for pasaffe # Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 # This file is distributed under the same license as the pasaffe package. # FIRST AUTHOR , 2013. # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2014-02-07 20:14+0000\n" "Last-Translator: Marc Deslauriers \n" "Language-Team: Korean \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-29 05:52+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "상위 폴더:" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "폴더 이름:" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "이 새 폴더의 상위 폴더" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "암호가 일치하지 않습니다! 다시 시도해주십시오." #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "닫기 전에 암호 데이터 베이스에 변경 사항을 저장하시겠습니까?" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "새 암호 확인:" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " Litty https://launchpad.net/~litty\n" " Marc Deslauriers https://launchpad.net/~mdeslaur" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "사용자 이름 복사" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "암호 복사" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "URL 열기" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "사용자 이름 복사" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "암호 복사" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "이전 결과" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "다음 결과" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "" #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Pasaffe 암호 관리자" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "" #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Pasaffe에 오신 것을 환영합니다!" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "" #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "마지막 저장 날짜: %s\n" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "데이터 베이스 버전: %s\n" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "" #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "Pasaffe 설정" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "변경 사항 자동 저장" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "잠금" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "암호 생성기" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "폴더 설정" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/nl.po0000644000175000017500000004015200000000000016116 0ustar00mdeslaurmdeslaur00000000000000# Dutch translation for pasaffe # Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 # This file is distributed under the same license as the pasaffe package. # FIRST AUTHOR , 2011. # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2014-02-07 20:12+0000\n" "Last-Translator: Marc Deslauriers \n" "Language-Team: Dutch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-29 05:52+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "welkom bij Pasaffe!" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "Voer wachtwoord in:" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "Bevestig wachtwoord:" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "Foutief wachtwoord! Probeer nog een keer." #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "Uw wijzigingen zullen verloren gaan als u ze niet opslaat." #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "foutmelding" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "Oud wachtwoord:" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "Nieuw wachtwoord:" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "Bevestig nieuw wachtwoord:" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " Marc Deslauriers https://launchpad.net/~mdeslaur\n" " Wim Champagne https://launchpad.net/~fng-deactivatedaccount-" "deactivatedaccount" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "Titel:" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "Gebruikersnaam:" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "Wachtwoord:" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "Opmerkingen:" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "URL-adres:" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "_Bestand" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "Vergrendelen" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "_Bewerken" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "_Beeld" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "Database Informatie" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "_Help" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "Wijzigingen opslaan" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "Opslaan" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "Bewerken" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "" #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Pasaffe wachtwoord manager" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "" #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Welkom bij Pasaffe!" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "" #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" "Database Informatie\n" "\n" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "Het nieuwe wachtwoord kan niet leeg zijn! Probeer nog een keer." #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "" ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1607702281.064041 pasaffe-0.57/po/pasaffe.pot0000664000175000017500000003644300000000000017310 0ustar00mdeslaurmdeslaur00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-12-11 10:58-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" #. Code for other initialization actions should be added here. #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:2 ../data/ui/PasswordEntryDialog.ui.h:1 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "" #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/NewDatabaseDialog.ui.h:2 ../data/ui/EditFolderDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "" #: ../pasaffe.desktop.in.h:1 ../data/ui/PasaffeWindow.ui.h:1 msgid "Pasaffe" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:551 ../data/ui/EditDetailsDialog.ui.h:12 msgid "URL:" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "" #: ../pasaffe/PasaffeWindow.py:567 ../data/ui/EditDetailsDialog.ui.h:9 msgid "Username:" msgstr "" #: ../pasaffe/PasaffeWindow.py:575 ../data/ui/EditDetailsDialog.ui.h:10 msgid "Password:" msgstr "" #: ../pasaffe/PasaffeWindow.py:591 ../data/ui/EditDetailsDialog.ui.h:11 msgid "Notes:" msgstr "" #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "" #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" #: ../pasaffe/PasaffeWindow.py:1555 ../data/ui/NewDatabaseDialog.ui.h:7 msgid "Passwords don't match! Please try again." msgstr "" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "" #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:7 ../data/ui/PasaffeWindow.ui.h:21 msgid "_Help" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:8 ../data/ui/PasaffeWindow.ui.h:6 msgid "_Close" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "" #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607700826.8340302 pasaffe-0.57/po/ru.po0000644000175000017500000005137200000000000016141 0ustar00mdeslaurmdeslaur00000000000000# Russian translation for pasaffe # Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 # This file is distributed under the same license as the pasaffe package. # FIRST AUTHOR , 2012. # msgid "" msgstr "" "Project-Id-Version: pasaffe\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2020-01-28 14:20-0500\n" "PO-Revision-Date: 2019-09-25 07:00+0000\n" "Last-Translator: Marc Deslauriers \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2020-01-29 05:52+0000\n" "X-Generator: Launchpad (build b8d1327fd820d6bf500589d6da587d5037c7d88e)\n" "X-Poedit-Country: RUSSIAN FEDERATION\n" "X-Poedit-Language: Russian\n" #: ../data/ui/EditFolderDialog.ui.h:1 ../data/ui/NewDatabaseDialog.ui.h:1 #: ../data/ui/PasswordEntryDialog.ui.h:1 ../data/ui/SaveChangesDialog.ui.h:2 #: ../data/ui/NewPasswordDialog.ui.h:1 ../data/ui/EditDetailsDialog.ui.h:1 msgid "_Cancel" msgstr "О_тменить" #: ../data/ui/EditFolderDialog.ui.h:2 ../data/ui/NewDatabaseDialog.ui.h:2 #: ../data/ui/PasswordEntryDialog.ui.h:2 ../data/ui/NewPasswordDialog.ui.h:2 #: ../data/ui/EditDetailsDialog.ui.h:2 msgid "_OK" msgstr "_ОК" #: ../data/ui/EditFolderDialog.ui.h:3 msgid "Edit folder" msgstr "Изменить папку" #: ../data/ui/EditFolderDialog.ui.h:4 msgid "Parent folder:" msgstr "Родительская папка:" #: ../data/ui/EditFolderDialog.ui.h:5 msgid "Folder name:" msgstr "Имя папки:" #: ../data/ui/EditFolderDialog.ui.h:6 msgid "The parent folder for this new folder" msgstr "Родительская папка для этой новой папки" #: ../data/ui/NewDatabaseDialog.ui.h:3 msgid "Welcome to Pasaffe!" msgstr "Добро пожаловать в Pasaffe!" #: ../data/ui/NewDatabaseDialog.ui.h:4 msgid "" "No existing database was found.\n" "A new one will be created.\n" "Please enter a master password to protect it." msgstr "" "База данных не найдена.\n" "Будет создана новая база данных.\n" "Укажите главный пароль для её защиты." #: ../data/ui/NewDatabaseDialog.ui.h:7 ../pasaffe/PasaffeWindow.py:1555 msgid "Passwords don't match! Please try again." msgstr "Пароли не совпадают! Попробуйте снова." #: ../data/ui/NewDatabaseDialog.ui.h:8 msgid "Enter password:" msgstr "Введите пароль:" #: ../data/ui/NewDatabaseDialog.ui.h:9 msgid "Confirm password:" msgstr "Подтвердите пароль:" #: ../data/ui/PasswordEntryDialog.ui.h:3 msgid "Please enter your master password:" msgstr "Введите свой главный пароль:" #: ../data/ui/PasswordEntryDialog.ui.h:4 msgid "Invalid password! Please try again." msgstr "Неправильный пароль! Попробуйте снова." #: ../data/ui/SaveChangesDialog.ui.h:1 msgid "Close without Saving" msgstr "Закрыть без сохранения" #: ../data/ui/SaveChangesDialog.ui.h:3 ../data/ui/PasaffeWindow.ui.h:3 msgid "_Save" msgstr "_Сохранить" #: ../data/ui/SaveChangesDialog.ui.h:4 msgid "" "Save changes to your password database before closing?" msgstr "" "Сохранить изменения в базе данных паролей перед закрытием?" #: ../data/ui/SaveChangesDialog.ui.h:5 msgid "Your changes will be lost if you don't save them." msgstr "Если не сохранить изменения, они будут утеряны." #: ../data/ui/NewPasswordDialog.ui.h:3 msgid "Change master password" msgstr "Изменить главный пароль" #: ../data/ui/NewPasswordDialog.ui.h:4 msgid "" "Enter your old master password,\n" "and a new master password.\n" "You must confirm the new password." msgstr "" "Введите свой старый главный пароль\n" "и новый главный пароль.\n" "Затем подтвердите новый пароль." #: ../data/ui/NewPasswordDialog.ui.h:7 msgid "error message" msgstr "сообщение об ошибке" #: ../data/ui/NewPasswordDialog.ui.h:8 msgid "Old password:" msgstr "Старый пароль:" #: ../data/ui/NewPasswordDialog.ui.h:9 msgid "New password:" msgstr "Новый пароль:" #: ../data/ui/NewPasswordDialog.ui.h:10 msgid "Confirm new password:" msgstr "Подтвердите новый пароль:" #: ../data/ui/AboutPasaffeDialog.ui.h:1 msgid "http://www.launchpad.net/pasaffe" msgstr "http://www.launchpad.net/pasaffe" #: ../data/ui/AboutPasaffeDialog.ui.h:2 msgid "translator-credits" msgstr "" "Launchpad Contributions:\n" " Aleksey Kabanov https://launchpad.net/~ak099\n" " Eugene Roskin https://launchpad.net/~lowrider\n" " Karma Dorje https://launchpad.net/~taaroa\n" " Marc Deslauriers https://launchpad.net/~mdeslaur" #: ../pasaffe/__init__.py:39 msgid "Show debug messages (-vv debugs pasaffe_lib also)" msgstr "Показывать отладочные сообщения (-vv — отлаживать также pasaffe_lib)" #: ../pasaffe/__init__.py:42 msgid "use database FILE" msgstr "использовать ФАЙЛ базы данных" #: ../pasaffe/__init__.py:45 msgid "set database as default" msgstr "установить базу данных по умолчанию" #: ../data/ui/EditDetailsDialog.ui.h:3 msgid "Edit entry" msgstr "Изменить запись" #: ../data/ui/EditDetailsDialog.ui.h:4 msgid "The name for this entry" msgstr "Имя для этой записи" #: ../data/ui/EditDetailsDialog.ui.h:5 msgid "The folder for this entry" msgstr "Эта папка пуста" #: ../data/ui/EditDetailsDialog.ui.h:6 msgid "The entry's username" msgstr "Имя пользователя для записи" #: ../data/ui/EditDetailsDialog.ui.h:7 msgid "Title:" msgstr "Заголовок:" #: ../data/ui/EditDetailsDialog.ui.h:8 msgid "Folder:" msgstr "Папка:" #: ../data/ui/EditDetailsDialog.ui.h:9 ../pasaffe/PasaffeWindow.py:567 msgid "Username:" msgstr "Имя пользователя:" #: ../data/ui/EditDetailsDialog.ui.h:10 ../pasaffe/PasaffeWindow.py:575 msgid "Password:" msgstr "Пароль:" #: ../data/ui/EditDetailsDialog.ui.h:11 ../pasaffe/PasaffeWindow.py:591 msgid "Notes:" msgstr "Примечания:" #: ../data/ui/EditDetailsDialog.ui.h:12 ../pasaffe/PasaffeWindow.py:551 msgid "URL:" msgstr "URL:" #: ../data/ui/EditDetailsDialog.ui.h:13 msgid "The website for this entry" msgstr "Веб-сайт для этой записи" #: ../data/ui/EditDetailsDialog.ui.h:14 msgid "Pick a random password" msgstr "Выбрать случайный пароль" #: ../bin/pasaffe-cli.py:94 msgid "run with debug" msgstr "" #: ../bin/pasaffe-cli.py:98 msgid "specify alternate GPass database file" msgstr "" #: ../bin/pasaffe-cli.py:104 msgid "add a new entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:109 msgid "replace an entry in the database" msgstr "" #: ../bin/pasaffe-cli.py:114 msgid "remove an entry from the database" msgstr "" #: ../bin/pasaffe-cli.py:119 ../bin/pasaffe-cli.py:125 msgid "print out a full entry in the database, except the password" msgstr "" #: ../bin/pasaffe-cli.py:131 msgid "look for --entry as a substring (default: False)" msgstr "" #: ../bin/pasaffe-cli.py:137 msgid "print out the group field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:142 msgid "print out the user field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:147 msgid "print out the Notes filed of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:152 msgid "print out the password for the given entry" msgstr "" #: ../bin/pasaffe-cli.py:157 msgid "print out the URL field of the given entry" msgstr "" #: ../bin/pasaffe-cli.py:162 msgid "create a new Pasaffe database" msgstr "" #: ../bin/pasaffe-cli.py:166 msgid "specify Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:170 msgid "specify new Pasaffe database master password" msgstr "" #: ../bin/pasaffe-cli.py:175 msgid "quiet messages" msgstr "" #: ../bin/pasaffe-cli.py:179 msgid "act on this database entry" msgstr "" #: ../bin/pasaffe-cli.py:183 msgid "replacement entry string" msgstr "" #: ../bin/pasaffe-cli.py:187 msgid "group for this entry" msgstr "" #: ../bin/pasaffe-cli.py:191 msgid "userId for this entry" msgstr "" #: ../bin/pasaffe-cli.py:195 msgid "password/passphrase for this entry" msgstr "" #: ../bin/pasaffe-cli.py:199 msgid "URL for this entry" msgstr "" #: ../bin/pasaffe-cli.py:203 msgid "free-format notes for this entry" msgstr "" #: ../bin/pasaffe-cli.py:256 msgid "when adding an entry, at least the userId must be provided" msgstr "" #: ../bin/pasaffe-cli.py:264 msgid "" "--repl MUST be used with at least one of --newentry, --group, --user, --" "pswd, --url, or --notes" msgstr "" #: ../bin/pasaffe-cli.py:279 msgid "" "ERROR: only one of --createdb, --list, --add, --repl, or --del can be " "specified" msgstr "" #: ../bin/pasaffe-cli.py:288 msgid "cannot use --list* with any of --add, --del, or --repl" msgstr "" #: ../bin/pasaffe-cli.py:295 msgid "one of --entry, --createdb, or --genpswd MUST be provided" msgstr "" #: ../bin/pasaffe-cli.py:330 msgid "" "ERROR: --createdb requested, but there is already a file with the same name" msgstr "" #: ../bin/pasaffe-cli.py:335 ../bin/pasaffe-cli.py:509 msgid "Password> " msgstr "" #: ../bin/pasaffe-cli.py:341 msgid "error creating new database" msgstr "" #: ../bin/pasaffe-cli.py:352 msgid "Could not read the database, bad password?" msgstr "" #: ../bin/pasaffe-cli.py:362 msgid "need --entry to replace fields in an entry" msgstr "" #: ../bin/pasaffe-cli.py:367 msgid "did not find any matching entry" msgstr "" #: ../bin/pasaffe-cli.py:370 msgid "found more than one entry to change, cannot change" msgstr "" #: ../bin/pasaffe-cli.py:397 msgid "need --entry to add an entry to the database" msgstr "" #: ../bin/pasaffe-cli.py:400 msgid "provided --entry already exists in the database" msgstr "" #: ../bin/pasaffe-cli.py:429 #, python-format msgid "did not find hits for %s in the database" msgstr "" #: ../bin/pasaffe-cli.py:472 msgid "provided --entry was not found in the database" msgstr "" #: ../bin/pasaffe-cli.py:475 msgid "provided --entry occurs multiple times, cannot delete" msgstr "" #: ../bin/pasaffe-cli.py:496 msgid "terminating run" msgstr "" #: ../bin/pasaffe-cli.py:521 msgid "" "\n" "ERROR: Could not locate database file!" msgstr "" #. noqa: F401 #. noqa: F401 #: ../data/ui/PasaffeWindow.ui.h:1 ../pasaffe.desktop.in.h:1 msgid "Pasaffe" msgstr "Pasaffe" #: ../data/ui/PasaffeWindow.ui.h:2 msgid "_File" msgstr "_Файл" #: ../data/ui/PasaffeWindow.ui.h:4 msgid "Lock" msgstr "Блокировать" #: ../data/ui/PasaffeWindow.ui.h:5 msgid "Change Master Password" msgstr "Изменить главный пароль" #: ../data/ui/PasaffeWindow.ui.h:6 ../data/ui/PreferencesPasaffeDialog.ui.h:8 msgid "_Close" msgstr "_Закрыть" #: ../data/ui/PasaffeWindow.ui.h:7 msgid "_Edit" msgstr "_Правка" #: ../data/ui/PasaffeWindow.ui.h:8 msgid "Add Entry" msgstr "Добавить запись" #: ../data/ui/PasaffeWindow.ui.h:9 msgid "Add Folder" msgstr "Добавить папку" #: ../data/ui/PasaffeWindow.ui.h:10 msgid "Clone" msgstr "Клонировать" #: ../data/ui/PasaffeWindow.ui.h:11 msgid "_Delete" msgstr "_Удалить" #: ../data/ui/PasaffeWindow.ui.h:12 msgid "Find" msgstr "Поиск" #: ../data/ui/PasaffeWindow.ui.h:13 msgid "Copy URL" msgstr "Копировать URL" #: ../data/ui/PasaffeWindow.ui.h:14 msgid "Copy Username" msgstr "Копировать имя пользователя" #: ../data/ui/PasaffeWindow.ui.h:15 msgid "Copy Password" msgstr "Копировать пароль" #: ../data/ui/PasaffeWindow.ui.h:16 msgid "_Preferences" msgstr "_Параметры" #: ../data/ui/PasaffeWindow.ui.h:17 msgid "_View" msgstr "_Вид" #: ../data/ui/PasaffeWindow.ui.h:18 msgid "Display Secrets" msgstr "Показывать секретные данные" #: ../data/ui/PasaffeWindow.ui.h:19 msgid "Open URL" msgstr "Открыть URL" #: ../data/ui/PasaffeWindow.ui.h:20 msgid "Database Information" msgstr "Информация о базе данных" #: ../data/ui/PasaffeWindow.ui.h:21 ../data/ui/PreferencesPasaffeDialog.ui.h:7 msgid "_Help" msgstr "_Справка" #: ../data/ui/PasaffeWindow.ui.h:22 msgid "Contents" msgstr "Содержание" #: ../data/ui/PasaffeWindow.ui.h:23 msgid "_About" msgstr "_О приложении" #: ../data/ui/PasaffeWindow.ui.h:24 msgid "Save changes" msgstr "Сохранить изменения" #: ../data/ui/PasaffeWindow.ui.h:25 msgid "Save" msgstr "Сохранить" #: ../data/ui/PasaffeWindow.ui.h:26 msgid "Add a new entry" msgstr "Добавить новую запись" #: ../data/ui/PasaffeWindow.ui.h:27 msgid "Add" msgstr "Добавить" #: ../data/ui/PasaffeWindow.ui.h:28 msgid "Add a new folder" msgstr "Добавить новую папку" #: ../data/ui/PasaffeWindow.ui.h:29 msgid "Edit the selected entry" msgstr "Изменить выбранную запись" #: ../data/ui/PasaffeWindow.ui.h:30 msgid "Edit" msgstr "Правка" #: ../data/ui/PasaffeWindow.ui.h:31 msgid "Delete the selected entry" msgstr "Удалить выбранную запись" #: ../data/ui/PasaffeWindow.ui.h:32 msgid "Remove" msgstr "Удалить" #: ../data/ui/PasaffeWindow.ui.h:33 msgid "Open the URL" msgstr "Открыть URL" #: ../data/ui/PasaffeWindow.ui.h:34 msgid "Copy the username" msgstr "Копировать имя пользователя" #: ../data/ui/PasaffeWindow.ui.h:35 msgid "Copy the password" msgstr "Копировать пароль" #: ../data/ui/PasaffeWindow.ui.h:36 msgid "Find entries" msgstr "Найти записи" #: ../data/ui/PasaffeWindow.ui.h:37 msgid "Display secrets" msgstr "Показывать секретные данные" #: ../data/ui/PasaffeWindow.ui.h:38 msgid "icon" msgstr "значок" #: ../data/ui/PasaffeWindow.ui.h:39 msgid "name" msgstr "имя" #: ../data/ui/PasaffeWindow.ui.h:40 msgid "Close find" msgstr "Закрыть поиск" #: ../data/ui/PasaffeWindow.ui.h:41 msgid "Previous result" msgstr "Предыдущий результат" #: ../data/ui/PasaffeWindow.ui.h:42 msgid "Next result" msgstr "Следующий результат" #: ../data/ui/PasaffeWindow.ui.h:43 msgid "The password database is currently locked." msgstr "База паролей сейчас заблокирована." #: ../data/ui/PasaffeWindow.ui.h:44 msgid "_Quit" msgstr "В_ыйти" #: ../data/ui/PasaffeWindow.ui.h:45 msgid "Unlock" msgstr "Разблокировать" #. noqa: E722 #: ../pasaffe_lib/helpers.py:396 msgid "error running apg" msgstr "" #: ../pasaffe.desktop.in.h:2 msgid "Pasaffe password manager" msgstr "Менеджер паролей Pasaffe" #: ../pasaffe/PasaffeWindow.py:253 ../pasaffe/PasaffeWindow.py:273 #: ../pasaffe/PasaffeWindow.py:541 ../pasaffe/PasaffeWindow.py:1096 msgid "[Untitled]" msgstr "" #: ../pasaffe/PasaffeWindow.py:563 msgid "Secrets are currently hidden." msgstr "Секретные данные сейчас скрыты." #: ../pasaffe/PasaffeWindow.py:601 msgid "Last updated:" msgstr "Последнее обновление:" #: ../pasaffe/PasaffeWindow.py:611 msgid "Password updated:" msgstr "Обновление пароля:" #: ../pasaffe/PasaffeWindow.py:644 msgid "Welcome to Pasaffe!" msgstr "Добро пожаловать в Pasaffe!" #: ../pasaffe/PasaffeWindow.py:649 msgid "" "Pasaffe is an easy to use\n" "password manager." msgstr "" "Pasaffe — это простой в использовании\n" "менеджер паролей." #: ../pasaffe/PasaffeWindow.py:664 msgid "This is a folder." msgstr "Это папка." #: ../pasaffe/PasaffeWindow.py:823 msgid "New Folder" msgstr "Новая папка" #: ../pasaffe/PasaffeWindow.py:873 #, python-format msgid "" "Are you sure you wish to remove \"%s\"?\n" "\n" msgstr "" "Вы действительно хотите удалить \"%s\"?\n" "\n" #: ../pasaffe/PasaffeWindow.py:876 msgid "Contents of the entry will be lost.\n" msgstr "Содержимое записи будет утеряно.\n" #: ../pasaffe/PasaffeWindow.py:896 #, python-format msgid "" "Are you sure you wish to remove folder \"%s\"?\n" "\n" msgstr "" "Вы действительно хотите удалить папку \"%s\"?\n" "\n" #: ../pasaffe/PasaffeWindow.py:899 msgid "All entries in this folder will be lost.\n" msgstr "Все записи в этой папке будут потеряны.\n" #: ../pasaffe/PasaffeWindow.py:962 msgid "New entry" msgstr "" #: ../pasaffe/PasaffeWindow.py:1093 msgid "New folder" msgstr "" #: ../pasaffe/PasaffeWindow.py:1444 msgid "" "Database Information\n" "\n" msgstr "" "Информация о базе данных\n" "\n" #: ../pasaffe/PasaffeWindow.py:1445 #, python-format msgid "Number of entries: %s\n" msgstr "Число записей: %s\n" #: ../pasaffe/PasaffeWindow.py:1449 #, python-format msgid "Last saved by: %s\n" msgstr "Последним сохранил пользователь: %s\n" #: ../pasaffe/PasaffeWindow.py:1452 #, python-format msgid "Last saved on host: %s\n" msgstr "Последнее сохранение на компьютере: %s\n" #: ../pasaffe/PasaffeWindow.py:1454 #, python-format msgid "Last save date: %s\n" msgstr "Дата последнего сохранения: %s\n" #: ../pasaffe/PasaffeWindow.py:1457 #, python-format msgid "Database version: %s\n" msgstr "Версия базы данных: %s\n" #: ../pasaffe/PasaffeWindow.py:1460 #, python-format msgid "Application used: %s\n" msgstr "Использовалось приложение: %s\n" #: ../pasaffe/PasaffeWindow.py:1463 #, python-format msgid "" "Database location:\n" "%s\n" msgstr "" "Расположение базы данных:\n" "%s\n" #: ../pasaffe/PasaffeWindow.py:1560 msgid "New password cannot be blank! Please try again." msgstr "Новый пароль не должен быть пустым! Попробуйте снова." #: ../pasaffe/PasaffeWindow.py:1565 msgid "Old password is invalid! Please try again." msgstr "Старый пароль введён неверно! Попробуйте снова." #: ../data/ui/PreferencesPasaffeDialog.ui.h:1 msgid "start expanded" msgstr "развернуть все при запуске" #: ../data/ui/PreferencesPasaffeDialog.ui.h:2 msgid "start collapsed" msgstr "свернуть все при запуске" #: ../data/ui/PreferencesPasaffeDialog.ui.h:3 msgid "are remembered from last save" msgstr "как при последнем сохранении" #: ../data/ui/PreferencesPasaffeDialog.ui.h:4 msgid "edits entry" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:5 msgid "copies password" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:6 msgid "Pasaffe Preferences" msgstr "Параметры Pasaffe" #: ../data/ui/PreferencesPasaffeDialog.ui.h:9 msgid "Automatically save changes" msgstr "Автоматически сохранять изменения" #: ../data/ui/PreferencesPasaffeDialog.ui.h:10 msgid "Display secrets by default" msgstr "Не скрывать секретные данные по умолчанию" #: ../data/ui/PreferencesPasaffeDialog.ui.h:11 msgid "Only passwords are secret" msgstr "Секретным является только пароль" #: ../data/ui/PreferencesPasaffeDialog.ui.h:12 msgid "Secrets" msgstr "Секретные данные" #: ../data/ui/PreferencesPasaffeDialog.ui.h:13 msgid "Lock database after" msgstr "Заблокировать базу данных через" #: ../data/ui/PreferencesPasaffeDialog.ui.h:14 msgid "minutes" msgstr "минут" #: ../data/ui/PreferencesPasaffeDialog.ui.h:15 msgid "Lock" msgstr "Заблокировать" #: ../data/ui/PreferencesPasaffeDialog.ui.h:16 msgid "Passwords are" msgstr "Пароли" #: ../data/ui/PreferencesPasaffeDialog.ui.h:17 msgid "characters long" msgstr "символов в длину" #: ../data/ui/PreferencesPasaffeDialog.ui.h:18 msgid "Password Generator" msgstr "Генератор паролей" #: ../data/ui/PreferencesPasaffeDialog.ui.h:19 msgid "Folders" msgstr "Папки" #: ../data/ui/PreferencesPasaffeDialog.ui.h:20 msgid "Double-click" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:21 msgid "Display usernames in tree" msgstr "" #: ../data/ui/PreferencesPasaffeDialog.ui.h:22 msgid "Folder Preferences" msgstr "Параматеры папок" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702281.5000415 pasaffe-0.57/setup.py0000755000175000017500000000570000000000000016244 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # # DO NOT TOUCH THIS (HEAD TO THE SECOND PART) import os import sys from glob import glob try: import DistUtilsExtra.auto except ImportError: print('To build pasaffe you need ' + 'https://launchpad.net/python-distutils-extra', file=sys.stderr) sys.exit(1) assert DistUtilsExtra.auto.__version__ >= '2.18', \ 'needs DistUtilsExtra.auto >= 2.18' def update_config(values={}): oldvalues = {} try: fin = open('pasaffe_lib/pasaffeconfig.py', 'r') fout = open(fin.name + '.new', 'w') for line in fin: fields = line.split(' = ') # Separate variable from value if fields[0] in values: oldvalues[fields[0]] = fields[1].strip() line = "%s = %s\n" % (fields[0], values[fields[0]]) fout.write(line) fout.flush() fout.close() fin.close() os.rename(fout.name, fin.name) except (OSError, IOError): print("ERROR: Can't find pasaffe_lib/pasaffeconfig.py") sys.exit(1) return oldvalues class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto): def run(self): values = {'__pasaffe_data_directory__': "'%s'" % (self.prefix + '/share/pasaffe/'), '__version__': "'%s'" % self.distribution.get_version()} # Older DistUtilsExtra put help files in /usr/share/gnome/help and # needed a ghelp: URL if DistUtilsExtra.auto.__version__ < '2.38': values['__help_prefix__'] = "'ghelp:'" values['__help_separator__'] = "'#'" previous_values = update_config(values) DistUtilsExtra.auto.install_auto.run(self) update_config(previous_values) # # YOU SHOULD MODIFY ONLY WHAT IS BELOW # DistUtilsExtra.auto.setup( name='pasaffe', version='0.57', license='GPL-3', author='Marc Deslauriers', author_email='marc.deslauriers@canonical.com', description='Password manager for GNOME', long_description='Pasaffe is an easy to use password manager for GNOME.', url='https://launchpad.net/pasaffe', data_files=[('share/mime/packages', glob('mime/*'))], cmdclass={'install': InstallAndUpdateDataDirectory} ) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/tests/0000775000175000017500000000000000000000000015671 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1607702282.3640425 pasaffe-0.57/tests/databases/0000775000175000017500000000000000000000000017620 5ustar00mdeslaurmdeslaur00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1414256062.0 pasaffe-0.57/tests/databases/README0000644000175000017500000000166700000000000020510 0ustar00mdeslaurmdeslaur00000000000000This directory contains some test databases that were created in other Password Safe compatible applications. All test databases use 'pasaffe' as the password. passwdsafe-510.psafe3: - Created with PasswdSafe 5.1.0 for Android pwsafe-331.psafe3: - Created with Password Safe for Windows v3.31 pw-gorilla-15363.psafe3: - Created with Password Gorilla v1.5.3.6.3 on Ubuntu 13.04 pw-gorilla-1537.psafe3: - Created with Password Gorilla v1.5.3.7 on Ubuntu 14.10 pasaffe-025.psafe3 - Created with Pasaffe 0.25 and the generate_pasaffe_db.py script gpass-050.gps - Created with GPass v0.5.0 on Ubuntu 10.04 keepassx-043.kdb - Created with KeePassX v0.4.3 on Ubuntu 14.04 keepassx-043.xml - Exported from KeePassX v0.4.3 on Ubuntu 14.04 figaro-079.xml - Exported from Figaro's Password Manager 2 v0.79 on Ubuntu 14.04 keepass2-224.kdbx - Created with KeePass2 v2.24 on Ubuntu 14.04 keepass2-224.xml - Exported from KeePass2 v2.24 on Ubuntu 14.04 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1391961459.0 pasaffe-0.57/tests/databases/figaro-079.xml0000600000175000017500000000140100000000000022110 0ustar00mdeslaurmdeslaur00000000000000 topcategory3username3hostname3password3This is notes Line 2category1topentry1username1hostname1password1This is a note Second line Unicode: ééé topentry2username2hostname2password2This is a note ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1392350107.0 pasaffe-0.57/tests/databases/gpass-050.gps0000600000175000017500000000126000000000000021737 0ustar00mdeslaurmdeslaur00000000000000"]$R^-HJչj06G\Х&W=[ HnzX P;XMH;i ݺkʍ&6mg#)L֛j9WwI.B =y"~ z.P`<nout/puK1 q31Tgן*T^>62Ip Qr( x:.u Q1HUj3 Oj]M ,(8g{\d d{NN ]ϿZ  ٌxx\uxM^Ai)H{_ x6RA+N&|UUS5.&BZu+s>:Xf5-=7@GVE> PZ; 7Rx\}ԶA%N_+ pB vQ3\x<<_d{|2Gv#;#yV vU8.,*էI {;~EkfUDh\(J9r-ׄ AThZ@1S0]b=yuKXyd2U8kvڷ} Vh{=!R4kq|ب>y7j`r062vψe}ӓs59:ïREdG'+]aJ}ZqM 4ߞC&b/Ud[ٙvQ%AQn3)7Rvt@wX𜻹ڌ+mAB@vï`K2} 6^=&LIsdέlP>%.S>ŖZ\UN{٤\-X! -nc鎼R 0ggE-~[yoYI)\eY|ԴZl|h0{DC.y#Tv?Z k:AE/avs!^8[u*PCdi}KtBT<!)CCsSs@KݮPZ4~=H2#Dvr=D48wcڔ칌 B9"/O&hoeN4`xqkaaP JaUאۿMQ ZyѸ6&\1k̈A<0G61O )qbW$5r'ؿɌ9}bTW]ѓGBWor{RnR(l'CwO'-!V斿PpuV}0TmО? obݟB< USþz%)A&KK3.I%|ij5\ &F "8Ў6 +1 Цņ9>EuIyf:*X[*#Ӛ3fCp޴)&B ʓ:O#XV4ZY}s!.J0bYSa<_0a(Uۓt/A cK2ܴhg#3poNuctMу}Mj5//~ KeePass 2014-02-15T13:56:35Z 2014-02-15T13:56:35Z 2014-02-15T13:56:35Z 365 2014-02-15T13:56:35Z -1 -1 False False True False False True AAAAAAAAAAAAAAAAAAAAAA== 2014-02-15T13:56:35Z AAAAAAAAAAAAAAAAAAAAAA== 2014-02-15T13:56:35Z 10 6291456 EqaKSVOh6kiRfnp4EzZxVw== EqaKSVOh6kiRfnp4EzZxVw== EqaKSVOh6kiRfnp4EzZxVw== NewDatabase 49 2014-02-15T13:56:35Z 2014-02-15T13:56:35Z 2014-02-15T13:58:41Z 2014-02-15T13:55:02Z False 15 2014-02-15T13:56:35Z True null null g8zhbEkQYUqAQKwQac7A2Q== g8zhbEkQYUqAQKwQac7A2Q== 0 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:58:44Z 2014-02-15T13:55:02Z False 1 2014-02-15T13:56:55Z Notes Notes Password Password Title Sample Entry URL http://keepass.info/ UserName User Name True 0 Target Window {USERNAME}{TAB}{PASSWORD}{TAB}{ENTER} 9JEN5OlpYE2tNPb48wpolg== 0 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:58:47Z 2014-02-15T13:55:02Z False 2 2014-02-15T13:56:55Z Password 12345 Title Sample Entry #2 URL http://keepass.info/help/kb/testform.html UserName Michael321 True 0 *Test Form - KeePass* NKWJOhHbbE2cLUc8YMMHVg== General 48 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:57:25Z 2014-02-15T13:55:02Z False 2 2014-02-15T13:56:55Z True null null AAAAAAAAAAAAAAAAAAAAAA== rBdJWFM6f0KMirwGPEaG1g== Windows 38 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:57:43Z 2014-02-15T13:55:02Z False 3 2014-02-15T13:56:55Z True null null AAAAAAAAAAAAAAAAAAAAAA== lnHUKTl+6EikvddUeWeFKQ== Network 3 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:58:33Z 2014-02-15T13:55:02Z False 4 2014-02-15T13:56:55Z True null null AAAAAAAAAAAAAAAAAAAAAA== U9/xzXo1S06T1Bg4VZj2Zw== 3 2014-02-15T13:58:30Z 2014-02-15T13:57:52Z 2014-02-15T13:58:30Z 2014-02-15T13:55:02Z False 1 2014-02-15T13:57:52Z Notes This is a note This is line two This is unicode: ééé This is line four Password password1 Title innetwork1 URL hostname1 UserName username1 True 0 isjxr5S0EEivwnFv9XCCzw== emptyinnetwork 48 2014-02-15T13:58:39Z 2014-02-15T13:58:35Z 2014-02-15T13:58:39Z 2014-02-15T05:00:00Z False 1 2014-02-15T13:58:35Z True null null AAAAAAAAAAAAAAAAAAAAAA== TJJHoo7zfEeQJ2e6K7E5hQ== Internet 1 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:57:23Z 2014-02-15T13:55:02Z False 1 2014-02-15T13:56:55Z True null null AAAAAAAAAAAAAAAAAAAAAA== yrMsAVlPW0eP5x+vv5glRw== eMail 19 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:57:23Z 2014-02-15T13:55:02Z False 1 2014-02-15T13:56:55Z True null null AAAAAAAAAAAAAAAAAAAAAA== sqvXMEDdwEeEeDvLwPt1KA== Homebanking 37 2014-02-15T13:56:55Z 2014-02-15T13:56:55Z 2014-02-15T13:57:24Z 2014-02-15T13:55:02Z False 1 2014-02-15T13:56:55Z True null null AAAAAAAAAAAAAAAAAAAAAA== +CmSUjzKK0aA1jrLcy+9mA== topempty1 48 2014-02-15T13:57:36Z 2014-02-15T13:57:32Z 2014-02-15T13:57:37Z 2014-02-15T05:00:00Z False 2 2014-02-15T13:57:32Z True null null AAAAAAAAAAAAAAAAAAAAAA== JWPtWi2PJUyY71EGFLQ2WA== topempty2 48 2014-02-15T13:57:42Z 2014-02-15T13:57:39Z 2014-02-15T13:57:42Z 2014-02-15T05:00:00Z False 1 2014-02-15T13:57:39Z True null null AAAAAAAAAAAAAAAAAAAAAA== ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1391952787.0 pasaffe-0.57/tests/databases/keepassx-043.kdb0000644000175000017500000000303400000000000022427 0ustar00mdeslaurmdeslaur00000000000000٢eK1^!fa7O1<'KSl Ú{LzX~`2?xNT I7 "\^`BzBlP_}lh_ U-!EȽ$ J=S4A:0.-)n$0Rj'Guf.^ G{#G_̝~_֐Josy%CvE]"2(o2:0J21'vK78ZADZГzZM3VC'=@f#鯟k)W/+}߾'т&_0WuA͜`SQ9USn|f"S}ucYAoIr-T 4[J .5H,#qM8Cc *xQ~ԅytR͛D|̂TEex==1p uH™hM<5̜BGε3X]ٗ*j,3޴k=YWk?߃Ӂ{B86Ylӹ4 fhV2u< V%R.קf%d !GcsFAaWR.-XMZ^a;h}:I DUxx35J{㽡O~%b` 89qdV Հ=m 7{NٙyFR˅o> g k1D|$hzmX=/W(QV$NFYĨG=RXism53ؖ/x-D>q7\D`'\8 ]wa,&׆h.ϞP*:P P!ӓ\7 2>%'Ɋhyvi`ҊsBՆ۔ 6LKٷji6뒭avG&(蠧SoEQ6b!h$pN '#)'m+th|5֒:2KýGb>$Ƥy(H] ~Y=]y cM;8nbN/dnJ9]hޟrWz`1I(TMq׉\S%K0ԘV1?+U t!ɬx@[<dheEJQ'8Yi6 c>T1L*"3܎~Nzvh3Q_cݥQEh}+Y>zXTBEO7U3z{syVORo:h[U%w] 7_a4iX70 |8/}CTq ɜ83ȌU3SإE3|'+ҒQ*D/)IiKP  ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1391952823.0 pasaffe-0.57/tests/databases/keepassx-043.xml0000644000175000017500000000265600000000000022500 0ustar00mdeslaurmdeslaur00000000000000 Internet 1 subgroup 0 emptygroup 0 subgroupentry1 username3 password3 hostname3 comment3 0 2014-02-09T08:32:01 2014-02-09T08:32:21 2014-02-09T08:32:21 Never topentry1 username1 password1 hostname1 This is a comment
Second line
Unicode: ééé
1 2014-02-09T08:29:59 2014-02-09T08:30:41 2014-02-09T08:30:31 Never
eMail 19 topentry2 username2 password2 hostname2 This is a comment
Second line
1 2014-02-09T08:30:46 2014-02-09T08:31:21 2014-02-09T08:31:21 Never
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/tests/databases/pasaffe-025.psafe30000644000175000017500000000225000000000000022631 0ustar00mdeslaurmdeslaur00000000000000PWS3AxY*؜=GDՁ [vrp8.̳3/WgA|T|/KoQU5x2<!5jsRέ߬CLAo?Ç]1mR+/Fm;GR PZ?)9eJx]J=(796Hd`7yd1HʼaNRWwgV ߧ@;n=Hn.K3e񑍶]+0ڑhDJ!#pic;$>E(6 d6PVZmŤ.VV6- ӡ-ƹ3خ/pw;oj |uYdXx*(p` 1OOk?I8C6eʋ0wzD/ .qkCuU,ko j<̗Ng֋R&>l,)cNrNIEnb72헍g)0%,s!f^Ei@#= YW,SrF 3cxLHfm&'oTu/cX!=!Fj9H[ qQ)~n¬a貤9т tn}cZk)`zSF*:UJǾ+fjv~J%̇ iʇ.mk 2$%SIOOx c UDEI0\ o"gʂW"F̀Y"^Dž #x[O bPWS3-EOFPWS3-EOFILB@[yG_k0@,\iTq././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1414255995.0 pasaffe-0.57/tests/databases/passwdsafe-510.psafe30000644000175000017500000000165000000000000023366 0ustar00mdeslaurmdeslaur00000000000000PWS3(a@&$i~H+a'C R*l+l(I3:V5A\%}ɡs%3b^#ܞkkl:nft&1 ;ɉk 6Y5tx!%&ϣź16 :K՗Ֆp,ḇ@x~D0|a漜B91.2w."3)_]!"D lLf(_ҁ`srKQ :a5ߐ$g4Ř3aÔI'9A >o$>#,dԟb|tfT 6%m* }%jpo,'{K_6aŠ&46Fh2⸒}=ϸ]7ˏEGӋkhDdk?ih,g0pw9"j tzm_^/rOg]eﲂd_~*X$TSEj X.isZRuB &?&wV6ɓXL`tu(8͋,xs]"bCbPY,[TRj6\I֏[7KknS{ g!\֊(X} N'So.L~:ڬѭGc(Dh?2>MY/ݙ5ժ5a\'gH<3-Յ++ɭ+d@NV# v!ޚO@0 LM퇎:ćO?Rx4ADobgAYiuF6hq$@onP.K gzB᪭7o L9PWS3-EOFPWS3-EOF\ܸp$րW UUO-/Fׁp././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/tests/databases/pw-gorilla-15363.psafe30000644000175000017500000000177000000000000023462 0ustar00mdeslaurmdeslaur00000000000000PWS3cffqbA{gxsV&hSyqjѭw:=#'ِ?zw,3X"Xiv?CZ88|g(ͱ`ǙG$q@!5V.Zsςy{ׂ4pXu:$,KL%NYέ Ws d<u,[wВ +#HISa'clMb-7(h~}+!Y;3^Zfy\V=5mܞRbq_I|VP.O-YDfa6U],lz3˷EReu2'XXI-~pZ9H)^! kKjJd)JlMHfP*J ȩ, <3@!QCmP658_yt1~Ky Ϸ/eZsTKؽMyL8JmIM .9d@Bo֯+ZZڠLq>`VD4Ӊ H=1 Y%N4*J݆ez<4?noOϣTVXyoϮEfHVvfӏ41𑡔/@`MOBK% M7,r s\·*>#d yKoiK`0ta-taQGN'cY}Zncj>1r^oS(  ]$Ķb%UO.'(j0!u]CˇpK eK(ECu"YKF+K5kO3$dg}`01Va]u!GZ=nvwO0l-{7_ wWVW7܏DIӯ̡h(KPWS3-EOFPWS3-EOF G:&97Z ;•qa*wFZK@ VF#fn`#8M/ZT:7/jOtǀ$=(LTpDИSeLaA|OLd+Sܶ*+O\'2fDt]נS41nJ/")+ǦˡZrBHa)2'aK:T~!3KóO=J ʚrԈҳ?lvRفG6, ~nzt%`O^&ߦwhcBLޭ;#u#UK.+({4qY,|`l H _(_z ؅@Ú zNڼ"jƒgH>Nt p.0sЛmЅ BJi=IaȦYPx F!0n7mq]tHT8E:eR4ˎK}RU wx]j?Xǐn9C}ض? qkoUys_!hdQ@FlX򠦁c-p`(ʉVuq2e ܸ:j^ms}4iG L̖/d7yop|ޝe(fb(/^~CYgLQoӫxp+a+>3|hrx+u]}m0V[,CZZJJ-gn?"N;^otƝe9@ɹwl-$-AN.R$JNH.GCAJ˛З(',(?8dZi*+Vuʃ6lFL*g92egkrdzJq;r]h;< ؤiUO.fOhb^c{Y*76iq@PWS3-EOFPWS3-EOF'M}vy>6>kV=^././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1376599457.0 pasaffe-0.57/tests/databases/pwsafe-331.psafe30000644000175000017500000000233000000000000022510 0ustar00mdeslaurmdeslaur00000000000000PWS3E 5O%?Z,c?EбyR1-L8c |+CA`Wf`Ȅ+'br=o2 cGKce(C; }SB"]M5w* u0,šl w{Uc,㮘BdnXk \fX.Vkwf ~O.p\](2Z6 2R7z954$rXLy9'y26/WFl}6dsNUpv`h>*K#Վ@JWKcqX֟= {{ PΦLggZ <чU$ϘrnD$,Jy2*rsxdQ0K]| PWS3-EOFPWS3-EOF|_ +oސ|ކ>$ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612649.0 pasaffe-0.57/tests/generate_pasaffe_db.py0000755000175000017500000000534400000000000022176 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 def get_test_data(): empty_folders = [['emptygroup1'], ['emptygroup1', 'emptygroup2'], ['emptygroup1', 'emptygroup2', 'emptygroup3'], ['level1group', 'level2group'], ['emptygroup1', 'test'], ['emptygroup1', 'with/slash']] entries = [{3: "topentry1", 4: "username1", 5: "This is a note", 6: "password1", 13: "http://www.example.com"}, {2: "level1group", 3: "level1entry", 4: "username1", 5: "This is a note", 6: "password1"}, {2: "level1group.level2group.level3group", 3: "level3entry", 4: "usernamelevel3", 6: "passwordlevel3"}] return entries, empty_folders def create_test_db(): passfile = PassSafeFile() passfile.new_db('pasaffe') entries, empty_folders = get_test_data() for entry in entries: uuid = passfile.new_entry() for key in entry.keys(): passfile.records[uuid][key] = entry[key] for folder in empty_folders: passfile.add_empty_folder(folder) return passfile if __name__ == '__main__': # This script can be used to create a test database # Simply run it with a filename, and a database will be created if len(sys.argv) < 2: print("Missing database output filename. Aborting.", file=sys.stderr) sys.exit(1) filename = sys.argv[1] if os.path.exists(filename): print("Output file '%s' already exists. Aborting." % filename, file=sys.stderr) sys.exit(1) passfile = create_test_db() passfile.writefile(filename) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612552.0 pasaffe-0.57/tests/test_dump_db.py0000644000175000017500000000372600000000000020722 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import subprocess sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) class TestDumpDB(unittest.TestCase): def setUp(self): pass def test_dump_db(self): out = subprocess.check_output(['bin/pasaffe-dump-db', '-q', '-f', './tests/databases/pasaffe-025.psafe3', '-m', 'pasaffe']) expected_out = b'''Entry: level1entry Username: username1 Password: password1 Notes: This is a note Entry: level3entry Username: usernamelevel3 Password: passwordlevel3 Entry: topentry1 Username: username1 Password: password1 URL: http://www.example.com Notes: This is a note ''' self.assertEqual(out, expected_out) def test_dump_db_verbose(self): out = subprocess.check_output(['bin/pasaffe-dump-db', '-f', './tests/databases/pasaffe-025.psafe3', '-m', 'pasaffe']) self.assertTrue(b'WARNING: this will display all password entries.' in out) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612873.0 pasaffe-0.57/tests/test_figaro_079.py0000644000175000017500000000673000000000000021154 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import time import struct sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.figaroxml import FigaroXML # noqa: E402 class TestFigaroXML079(unittest.TestCase): def setUp(self): self.passfile = FigaroXML('./tests/databases/figaro-079.xml') def _find_uuid(self, name): for uuid in self.passfile.records: if self.passfile.records[uuid][3] == name: return uuid return None def _get_time(self, uuid, entry): '''Returns a string of time''' if entry in self.passfile.records[uuid]: unpacked_time = struct.unpack( " # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import tempfile import shutil import subprocess sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 from test_figaro_079 import TestFigaroXML079 # noqa: E402 class TestFigaroXMLImport(TestFigaroXML079): def setUp(self): self.tempdir = tempfile.mkdtemp() self.imported_db = os.path.join(self.tempdir, 'imported.psafe3') subprocess.call(['bin/pasaffe-import-figaroxml', '-q', '-f', './tests/databases/figaro-079.xml', '-d', self.imported_db, '-y', '-m', 'pasaffe']) self.passfile = PassSafeFile(self.imported_db, 'pasaffe') def tearDown(self): if os.path.exists(self.tempdir): shutil.rmtree(self.tempdir) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612863.0 pasaffe-0.57/tests/test_gpass_050.py0000644000175000017500000001135100000000000021002 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import time import struct sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.gpassfile import GPassFile # noqa: E402 class TestGPass50(unittest.TestCase): def setUp(self): self.passfile = GPassFile('./tests/databases/gpass-050.gps', 'pasaffe') def _find_uuid(self, name): for uuid in self.passfile.records: if self.passfile.records[uuid][3] == name: return uuid return None def _get_time(self, uuid, entry): '''Returns a string of time''' if entry in self.passfile.records[uuid]: unpacked_time = struct.unpack( " # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import tempfile import shutil import subprocess sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 from test_gpass_050 import TestGPass50 # noqa: E402 class TestGPassImport(TestGPass50): def setUp(self): self.tempdir = tempfile.mkdtemp() self.imported_db = os.path.join(self.tempdir, 'imported.psafe3') subprocess.call(['bin/pasaffe-import-gpass', '-q', '-f', './tests/databases/gpass-050.gps', '-d', self.imported_db, '-y', '-p', 'pasaffe', '-m', 'pasaffe']) self.passfile = PassSafeFile(self.imported_db, 'pasaffe') def tearDown(self): if os.path.exists(self.tempdir): shutil.rmtree(self.tempdir) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1607701772.983423 pasaffe-0.57/tests/test_helpers.py0000644000175000017500000002361400000000000020750 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.helpers import folder_list_to_field # noqa: E402 from pasaffe_lib.helpers import field_to_folder_list # noqa: E402 from pasaffe_lib.helpers import folder_list_to_path # noqa: E402 from pasaffe_lib.helpers import folder_path_to_list # noqa: E402 from pasaffe_lib.helpers import PathEntry # noqa: E402 class TestHelpers(unittest.TestCase): def test_folder_list_to_field(self): folder_list = [[[], ""], [["foldera"], "foldera"], [["folder.a"], r"folder\.a"], # noqa: W605 [["foldera."], r"foldera\."], # noqa: W605 [[".foldera"], r"\.foldera"], # noqa: W605 [["foldera.", "folderb."], r"foldera\..folderb\."], # noqa: W605 [["foldera", "folderb"], "foldera.folderb"], [["folder.a", "folderb"], r"folder\.a.folderb"], # noqa: W605 [["foldera", "folder.b"], r"foldera.folder\.b"], # noqa: W605 [["folder.a", "folder.b"], r"folder\.a.folder\.b"], # noqa: W605 [["folder.a", "folder.b", "folder.c"], r"folder\.a.folder\.b.folder\.c"]] # noqa: W605 for (folder, field) in folder_list: self.assertEqual(folder_list_to_field(folder), field) def test_field_to_folder_list(self): folder_list = [["", []], ["foldera", ["foldera"]], [r"folder\.a", ["folder.a"]], # noqa: W605 [r"foldera\.", ["foldera."]], # noqa: W605 [r"\.foldera", [".foldera"]], # noqa: W605 [r"foldera\..folderb\.", ["foldera.", "folderb."]], # noqa: W605 ["foldera.folderb", ["foldera", "folderb"]], [r"folder\.a.folderb", ["folder.a", "folderb"]], # noqa: W605 [r"foldera.folder\.b", ["foldera", "folder.b"]], # noqa: W605 [r"folder\.a.folder\.b", ["folder.a", "folder.b"]], [r"folder\.a.folder\.b.folder\.c", ["folder.a", "folder.b", "folder.c"]]] for (field, folder) in folder_list: self.assertEqual(field_to_folder_list(field), folder) def test_folder_list_to_path(self): folder_list = [[[], "/"], [["foldera"], "/foldera/"], [["folder.a"], "/folder.a/"], [["folder/a"], r"/folder\/a/"], # noqa: W605 [["foldera."], "/foldera./"], [["foldera/"], r"/foldera\//"], # noqa: W605 [[".foldera"], "/.foldera/"], [["/foldera"], r"/\/foldera/"], # noqa: W605 [["foldera.", "folderb."], "/foldera./folderb./"], [["foldera/", "folderb/"], r"/foldera\//folderb\//"], # noqa: W605 [["foldera", "folderb"], "/foldera/folderb/"], [["folder.a", "folderb"], "/folder.a/folderb/"], [["folder/a", "folderb"], r"/folder\/a/folderb/"], # noqa: W605 [["foldera", "folder.b"], "/foldera/folder.b/"], [["foldera", "folder/b"], r"/foldera/folder\/b/"], [["folder.a", "folder.b"], "/folder.a/folder.b/"], [["folder/a", "folder/b"], r"/folder\/a/folder\/b/"], [["folder.a", "folder.b", "folder.c"], "/folder.a/folder.b/folder.c/"], [["folder/a", "folder/b", "folder/c"], r"/folder\/a/folder\/b/folder\/c/"]] for (folder, path) in folder_list: self.assertEqual(folder_list_to_path(folder), path) def test_folder_path_to_list(self): folder_list = [["/", []], ["/foldera/", ["foldera"]], ["/folder.a/", ["folder.a"]], [r"/folder\/a/", ["folder/a"]], # noqa: W605 ["/foldera./", ["foldera."]], [r"/foldera\//", ["foldera/"]], # noqa: W605 ["/.foldera/", [".foldera"]], [r"/\/foldera/", ["/foldera"]], # noqa: W605 ["/foldera./folderb./", ["foldera.", "folderb."]], [r"/foldera\//folderb\//", ["foldera/", "folderb/"]], # noqa: W605 ["/foldera/folderb/", ["foldera", "folderb"]], ["/folder.a/folderb/", ["folder.a", "folderb"]], [r"/folder\/a/folderb/", ["folder/a", "folderb"]], ["/foldera/folder.b/", ["foldera", "folder.b"]], [r"/foldera/folder\/b/", ["foldera", "folder/b"]], ["/folder.a/folder.b/", ["folder.a", "folder.b"]], [r"/folder\/a/folder\/b/", ["folder/a", "folder/b"]], ["/folder.a/folder.b/folder.c/", ["folder.a", "folder.b", "folder.c"]], [r"/folder\/a/folder\/b/folder\/c/", ["folder/a", "folder/b", "folder/c"]]] for (path, folder) in folder_list: self.assertEqual(folder_path_to_list(path), folder) def test_sort_name(self): pathentry = PathEntry(None, None, None) names = [["", "zzz", -1], [None, "zzz", -1], ["a", "zzz", -1], ["A", "zzz", -1], ["A", "a", -1], ["aaa", "z", -1], ["aaa", "Z", -1], ["aaa", "zzz", -1], ["z", "zzz", -1], ["aaa", "aaa", 0], ["zzz", "zzz", 0], [None, None, 0], [None, "", 0], ["", None, 0], ["zzz", "z", 1], ["z", "Z", 1], ["zzz", "", 1], ["zzz", None, 1]] for (first, second, result) in names: self.assertEqual(pathentry._sort_name(first, second), result) def test_sort_path(self): pathentry = PathEntry(None, None, None) paths = [[["zzz"], [], -1], [["zzz"], None, -1], [["a"], ["zzz"], -1], [["A"], ["zzz"], -1], [["A"], ["a"], -1], [["aaa"], ["z"], -1], [["aaa"], ["zzz"], -1], [["z"], ["zzz"], -1], [["a", "a"], ["a"], -1], [["a", "a", "a"], ["a"], -1], [["aaa"], ["aaa"], 0], [["zzz"], ["zzz"], 0], [None, None, 0], [None, [], 0], [[], None, 0], [["a"], ["a", "a"], 1], [["a", "a"], ["a", "a", "a"], 1], [["zzz"], ["z"], 1], [["zzz"], ["Z"], 1], [[], ["zzz"], 1], [None, ["zzz"], 1]] for (first, second, result) in paths: self.assertEqual(pathentry._sort_path(first, second), result) def test_sort_entries(self): test_entries = [["z", None], ["Z", None], ["a", None], ["A", None], ["aa", None], ["a", ["a"]], ["a", ["A"]], ["a", ["b"]], ["z", ["a", "b"]], ["a", ["a", "b"]], [None, ["a", "b", "c"]], ["c", ["a", "b"]]] test_results = [["a", ["A"]], [None, ["a", "b", "c"]], ["a", ["a", "b"]], ["c", ["a", "b"]], ["z", ["a", "b"]], ["a", ["a"]], ["a", ["b"]], ["A", None], ["a", None], ["aa", None], ["Z", None], ["z", None]] entries = [] results = [] for (name, path) in test_entries: new_entry = PathEntry(name, None, path) entries.append(new_entry) for (name, path) in test_results: new_result = PathEntry(name, None, path) results.append(new_result) # Now, sort them and check sorted_entries = sorted(entries) self.assertEqual(len(test_entries), len(results)) self.assertEqual(len(test_entries), len(entries)) self.assertEqual(len(test_entries), len(sorted_entries)) for i in range(len(test_entries)): self.assertEqual(sorted_entries[i], results[i]) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612581.0 pasaffe-0.57/tests/test_import_entry.py0000644000175000017500000000526600000000000022044 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import tempfile import shutil import subprocess sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 class TestImportEntry(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.test_db = os.path.join(self.tempdir, 'test.psafe3') shutil.copy('./tests/databases/pasaffe-025.psafe3', self.test_db) def tearDown(self): if os.path.exists(self.tempdir): shutil.rmtree(self.tempdir) def test_orig_db(self): passfile = PassSafeFile(self.test_db, 'pasaffe') self.assertEqual(len(passfile.records), 3) def test_import_entry(self): name = 'testimport1' url = 'http://www.launchpad.net/pasaffe' user = 'testuser' password = 'testpass' note = "This is a note" rc = subprocess.call(['bin/pasaffe-import-entry', '-q', '-f', self.test_db, '-m', 'pasaffe', '-e', name, '-l', url, '-u', user, '-p', password, '-n', note]) self.assertEqual(rc, 0) passfile = PassSafeFile(self.test_db, 'pasaffe') self.assertEqual(len(passfile.records), 4) # Locate the new entry for uuid in passfile.records: if passfile.records[uuid][3] == name: break self.assertEqual(passfile.records[uuid][3], name) self.assertEqual(passfile.records[uuid][4], user) self.assertEqual(passfile.records[uuid][5], note) self.assertEqual(passfile.records[uuid][6], password) self.assertEqual(passfile.records[uuid][13], url) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612405.0 pasaffe-0.57/tests/test_keepass2_224.py0000644000175000017500000001040100000000000021400 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import time import struct sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.keepassx import KeePassX # noqa: E402 class TestKeePass2224(unittest.TestCase): def setUp(self): self.passfile = KeePassX('./tests/databases/keepass2-224.xml') def _find_uuid(self, name): for uuid in self.passfile.records: if self.passfile.records[uuid][3] == name: return uuid return None def _get_time(self, uuid, entry): '''Returns a string of time''' if entry in self.passfile.records[uuid]: unpacked_time = struct.unpack( " # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import tempfile import shutil import subprocess sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 from test_keepass2_224 import TestKeePass2224 # noqa: E402 class TestKeePass2Import(TestKeePass2224): def setUp(self): self.tempdir = tempfile.mkdtemp() self.imported_db = os.path.join(self.tempdir, 'imported.psafe3') subprocess.call(['bin/pasaffe-import-keepassx', '-q', '-f', './tests/databases/keepass2-224.xml', '-d', self.imported_db, '-y', '-m', 'pasaffe']) self.passfile = PassSafeFile(self.imported_db, 'pasaffe') def tearDown(self): if os.path.exists(self.tempdir): shutil.rmtree(self.tempdir) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612503.0 pasaffe-0.57/tests/test_keepassx_043.py0000644000175000017500000001030100000000000021504 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import time import struct sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.keepassx import KeePassX # noqa: E402 class TestKeePassX043(unittest.TestCase): def setUp(self): self.passfile = KeePassX('./tests/databases/keepassx-043.xml') def _find_uuid(self, name): for uuid in self.passfile.records: if self.passfile.records[uuid][3] == name: return uuid return None def _get_time(self, uuid, entry): '''Returns a string of time''' if entry in self.passfile.records[uuid]: unpacked_time = struct.unpack( " # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import tempfile import shutil import subprocess sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 from test_keepassx_043 import TestKeePassX043 # noqa: E402 class TestKeePassXImport(TestKeePassX043): def setUp(self): self.tempdir = tempfile.mkdtemp() self.imported_db = os.path.join(self.tempdir, 'imported.psafe3') subprocess.call(['bin/pasaffe-import-keepassx', '-q', '-f', './tests/databases/keepassx-043.xml', '-d', self.imported_db, '-y', '-m', 'pasaffe']) self.passfile = PassSafeFile(self.imported_db, 'pasaffe') def tearDown(self): if os.path.exists(self.tempdir): shutil.rmtree(self.tempdir) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612935.0 pasaffe-0.57/tests/test_lint.py0000644000175000017500000000222300000000000020245 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2011-2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import unittest import subprocess class TestPylint(unittest.TestCase): def test_project_errors_only(self): '''run pylint in error only mode your code may well work even with pylint errors but have some unusual code''' subprocess.call(["pylint", '-E', 'pasaffe']) if __name__ == '__main__': 'you will get better results with nosetests' unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1457818199.0 pasaffe-0.57/tests/test_pasaffe-cli.py0000644000175000017500000002704700000000000021464 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2015 C de-Avillez # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MErcHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import subprocess import tempfile import shutil myPath = os.path.dirname(__file__) sys.path.insert(0, os.path.realpath(os.path.join(myPath, "..", "../bin", "../lib"))) # from pasaffe_lib.readdb import PassSafeFile def cliCmd(): return "%s/../bin/pasaffe-cli" % myPath def subproc(command, test): rc = 0 result = None try: result = subprocess.check_output(command).splitlines() except subprocess.CalledProcessError as err: print("%s failed with rc=%s" % (test, err.returncode)) print("%s" % err.output) rc = err.returncode result = None return result, rc def gen_pswd(size=16): command = ("%s" % cliCmd(), "--genpswd", "--pswdlen=%s" % size) db_pswd, rc = subproc(command, "gen_pswd") if rc == 0: db_pswd = db_pswd[0].decode('utf-8').strip() return db_pswd, rc def createDB(filename, password): command = (cliCmd(), "--createdb", "--masterpassword=%s" % password, "--file=%s" % filename) result, rc = subproc(command, "createDB") return result, rc def creatEntry(suffix): entry = "Entry%s" % suffix group = "Group%s" % suffix userId = "userId%s" % suffix passwd, rc = gen_pswd() password = passwd url = "https://url%s.com" % suffix notes = "Note%s line 1\nNote%s line 2" % (suffix, suffix) return [entry, group, userId, password, url, notes] def addEntry(db, pswd, entry=None, group=None, userid=None, password=None, url=None, notes=None): command = [] command.append(cliCmd()) # command.append("--debug") command.append("--add") command.append("--file=%s" % db) command.append("--masterpassword=%s" % pswd) command.append("--entry=%s" % entry) if group is not None: command.append("--group=%s" % group) if userid is not None: command.append("--user=%s" % userid) if password is not None: command.append("--pswd=%s" % password) if url is not None: command.append("--url=%s" % url) if notes is not None: command.append("--notes=%s" % notes) print("command=%s" % command) result, rc = subproc(command, "addEntry") return result, rc def replEntry(db, pswd, entry=None, newentry=None, group=None, userid=None, password=None, url=None, notes=None): command = [] command.append(cliCmd()) # command.append("--debug") command.append("--repl") command.append("--file=%s" % db) command.append("--masterpassword=%s" % pswd) command.append("--entry=%s" % entry) if newentry is not None: command.append("--newentry=%s" % newentry) if group is not None: command.append("--group=%s" % group) if userid is not None: command.append("--user=%s" % userid) if password is not None: command.append("--pswd=%s" % password) if url is not None: command.append("--url=%s" % url) if notes is not None: command.append("--notes=%s" % notes) print("command=%s" % command) result, rc = subproc(command, "replEntry") return result, rc def listEntry(db, pswd, entry="Dummy", fuzzy=False, lstUserId=False, lstPswd=False, lstGroup=False, lstURL=False, lstNotes=False, lstAll=False): command = [] command.append(cliCmd()) # command.append("--debug") command.append("--file=%s" % db) command.append("--masterpassword=%s" % pswd) command.append("--entry=%s" % entry) if lstAll: command.append("--listall") else: command.append("--list") if fuzzy: command.append("--fuzzy") if lstUserId: command.append("--listuser") if lstGroup: command.append("--listgroup") if lstPswd: command.append("--listpswd") if lstURL: command.append("--listurl") if lstNotes: command.append("--listnotes") print("command=%s" % command) result, rc = subproc(command, "listEntry") return result, rc class TestPasaffeCLI(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.db_name = os.path.join(self.tempdir, 'test_pasaffe-cli.psafe3') def tearDown(self): if os.path.exists(self.tempdir): shutil.rmtree(self.tempdir) def test_t01GenPassword(self): Len = 8 while Len < 32: password, rc = gen_pswd(size=Len) self.assertEqual(rc, 0, msg="call to genpswd failed") self.assertNotEqual(len(password), 0, msg="Failed to generate a password") self.assertEqual(len(password), Len, msg="Length of generated password does not" + " match requested length") Len += 1 def test_t02createDB(self): db_pswd, rc = gen_pswd() self.assertEqual(rc, 0, msg="call to genpswd failed") result, rc = createDB(self.db_name, db_pswd) self.assertEqual(rc, 0, msg="DB creation failed") self.assertTrue(os.path.isfile(self.db_name)) def test_t03addEntry(self): db_pswd, rc = gen_pswd() self.assertEqual(rc, 0, msg="call to genpswd failed") result, rc = createDB(self.db_name, db_pswd) self.assertEqual(rc, 0, msg="DB creation failed") values = creatEntry(1) result, rc = addEntry(self.db_name, db_pswd, entry=values[0]) self.assertNotEqual(rc, 0) values = creatEntry(2) result, rc = addEntry(self.db_name, db_pswd, entry=values[0], group=values[1]) self.assertNotEqual(rc, 0) values = creatEntry(3) result, rc = addEntry(self.db_name, db_pswd, entry=values[0], group=values[1], userid=values[2]) self.assertEqual(rc, 0) values = creatEntry(4) result, rc = addEntry(self.db_name, db_pswd, entry=values[0], group=values[1], userid=values[2], password=values[3]) self.assertEqual(rc, 0) values = creatEntry(5) result, rc = addEntry(self.db_name, db_pswd, entry=values[0], group=values[1], userid=values[2], password=values[3], url=values[4]) self.assertEqual(rc, 0) values = creatEntry(6) result, rc = addEntry(self.db_name, db_pswd, entry=values[0], group=values[1], userid=values[2], password=values[3], url=values[4], notes=values[5]) self.assertEqual(rc, 0) def test_t04replaceEntry(self): db_pswd, rc = gen_pswd() self.assertEqual(rc, 0, msg="call to genpswd failed") result, rc = createDB(self.db_name, db_pswd) self.assertEqual(rc, 0, msg="DB creation failed") values = creatEntry(1) result, rc = addEntry(self.db_name, db_pswd, entry=values[0], group=values[1], userid=values[2]) self.assertEqual(rc, 0, msg="adding entry 1 failed") result, rc = replEntry(self.db_name, db_pswd, entry=values[0], group=values[1], newentry="NewEntry1") self.assertEqual(rc, 0, msg="replacing entry string failed") result, rc = replEntry(self.db_name, db_pswd, entry="NewEntry1", group="NewGroup1") self.assertEqual(rc, 0, msg="replacing group string failed") result, rc = replEntry(self.db_name, db_pswd, entry="NewEntry1", group="Group1", userid="NewUser1") self.assertEqual(rc, 0, msg="replacing user string failed") result, rc = replEntry(self.db_name, db_pswd, entry="NewEntry1", group="Group1", userid="User1", password="Password1") self.assertEqual(rc, 0, msg="replacing password string failed") result, rc = replEntry(self.db_name, db_pswd, entry="NewEntry1", group="Group1", userid="User1", password="Password1", url="http://127.0.0.1") self.assertEqual(rc, 0, msg="replacing URL string failed") result, rc = replEntry(self.db_name, db_pswd, entry="NewEntry1", group="Group1", userid="User1", password="Password1", url="http://127.0.0.1", notes="This is a note for newEntry1") self.assertEqual(rc, 0, msg="replacing notes string failed") def test_t05listEntry(self): db_pswd, rc = gen_pswd() self.assertEqual(rc, 0, msg="call to genpswd failed") result, rc = createDB(self.db_name, db_pswd) self.assertEqual(rc, 0, msg="DB creation failed") result, rc = addEntry(self.db_name, db_pswd, entry="Entry1", group="Group1", userid="User1", password="Password1", url="http://127.0.0.1", notes="This is a note for Entry1") self.assertEqual(rc, 0, msg="adding entry 1 failed") result, rc = addEntry(self.db_name, db_pswd, entry="Entry2", group="Group2", userid="User2", password="Password2", url="http://127.0.0.2", notes="This is a note for Entry2") self.assertEqual(rc, 0, msg="adding entry 2 failed") result, rc = addEntry(self.db_name, db_pswd, entry="Entry3", group="Group3", userid="User3", password="Password3", url="http://127.0.0.3", notes="This is a note for Entry3") self.assertEqual(rc, 0, msg="adding entry 3 failed") result, rc = listEntry(self.db_name, db_pswd, entry="Entry1") self.assertEqual(rc, 0, "list Entry1 1/n failed") print("Result:%s" % result) result, rc = listEntry(self.db_name, db_pswd, entry="Entry2", ) self.assertEqual(rc, 0, "list Entry1 2/n failed") result, rc = listEntry(self.db_name, db_pswd, entry="Entry3", ) self.assertEqual(rc, 0, "list Entry1 2/n failed") def test_t06removeEntry(self): pass if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612480.0 pasaffe-0.57/tests/test_pasaffe_025.py0000644000175000017500000001167600000000000021306 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 class TestPasaffe025(unittest.TestCase): def setUp(self): self.passfile = PassSafeFile( './tests/databases/pasaffe-025.psafe3', 'pasaffe') def test_num_entries(self): self.assertEqual(len(self.passfile.records), 3) def test_empty_folders(self): empty_folders = [['emptygroup1'], ['emptygroup1', 'emptygroup2'], ['emptygroup1', 'emptygroup2', 'emptygroup3'], ['level1group', 'level2group'], ['emptygroup1', 'test'], ['emptygroup1', 'with/slash']] empty_fields = ['emptygroup1', 'emptygroup1.emptygroup2', 'emptygroup1.emptygroup2.emptygroup3', 'level1group.level2group', 'emptygroup1.test', 'emptygroup1.with/slash'] self.assertEqual(len(self.passfile.empty_folders), len(empty_fields)) self.assertEqual(self.passfile.get_empty_folders(), empty_folders) self.assertEqual(self.passfile.empty_folders, empty_fields) def test_get_database_version_string(self): self.assertEqual(self.passfile.get_database_version_string(), "03.0b") def test_get_database_uuid(self): self.assertEqual(self.passfile.header[1], b'\x04T@\xa5\xf9B\x08\xb8;\xda\xaf\xabV\x0f\xf5+') def test_get_saved_name(self): self.assertEqual(self.passfile.get_saved_name(), "mdeslaur") def test_get_saved_host(self): self.assertEqual(self.passfile.get_saved_host(), "aone") def test_get_saved_application(self): self.assertEqual(self.passfile.get_saved_application(), 'Pasaffe v0') def test_get_saved_date_string(self): self.assertEqual(self.passfile.get_saved_date_string(False), 'Tue, 30 Jul 2013 22:41:13') def test_entry_1(self): uuid = '2db404cb1cf50cb15b16e8df8629530b' self.assertFalse(2 in self.passfile.records[uuid]) self.assertEqual(self.passfile.get_folder_list(uuid), []) self.assertEqual(self.passfile.records[uuid][3], 'topentry1') self.assertEqual(self.passfile.records[uuid][4], 'username1') self.assertEqual(self.passfile.records[uuid][5], 'This is a note') self.assertEqual(self.passfile.records[uuid][6], 'password1') self.assertEqual(self.passfile.get_creation_time(uuid, False), 'Tue, 30 Jul 2013 22:41:13') self.assertEqual(self.passfile.records[uuid][13], 'http://www.example.com') def test_entry_2(self): uuid = '4b23ce8123e1aa44a1265209ecd6c7a3' self.assertEqual(self.passfile.records[uuid][2], 'level1group') self.assertEqual(self.passfile.get_folder_list(uuid), ['level1group']) self.assertEqual(self.passfile.records[uuid][3], 'level1entry') self.assertEqual(self.passfile.records[uuid][4], 'username1') self.assertEqual(self.passfile.records[uuid][5], 'This is a note') self.assertEqual(self.passfile.records[uuid][6], 'password1') self.assertEqual(self.passfile.get_creation_time(uuid, False), 'Tue, 30 Jul 2013 22:41:13') def test_entry_3(self): uuid = '8a213ac1aec45b187a4da87c142342a3' self.assertEqual(self.passfile.records[uuid][2], 'level1group.level2group.level3group') self.assertEqual(self.passfile.get_folder_list(uuid), ['level1group', 'level2group', 'level3group']) self.assertEqual(self.passfile.records[uuid][3], 'level3entry') self.assertEqual(self.passfile.records[uuid][4], 'usernamelevel3') self.assertEqual(self.passfile.records[uuid][5], '') self.assertEqual(self.passfile.records[uuid][6], 'passwordlevel3') self.assertEqual(self.passfile.get_creation_time(uuid, False), 'Tue, 30 Jul 2013 22:41:13') if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612956.0 pasaffe-0.57/tests/test_passwdsafe_510.py0000644000175000017500000001147000000000000022030 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 class TestPasswdSafeAndroid510(unittest.TestCase): def setUp(self): self.passfile = PassSafeFile( './tests/databases/passwdsafe-510.psafe3', 'pasaffe') def test_num_entries(self): self.assertEqual(len(self.passfile.records), 4) def test_empty_folders(self): self.assertEqual(len(self.passfile.empty_folders), 0) def test_get_database_version_string(self): self.assertEqual(self.passfile.get_database_version_string(), "03.0d") def test_get_database_uuid(self): self.assertEqual(self.passfile.header[1], b'b\xfb\x00\x00\x9al2\x13\x05\xb8\xccnh\x81L\xb5') def test_get_saved_name(self): self.assertEqual(self.passfile.get_saved_name(), 'User') def test_get_saved_host(self): self.assertEqual(self.passfile.get_saved_host(), 'Galaxy Nexus') def test_get_saved_application(self): self.assertEqual(self.passfile.get_saved_application(), 'PasswdSafe 5.1.0') def test_get_saved_date_string(self): self.assertEqual(self.passfile.get_saved_date_string(), 'Sat, 25 Oct 2014 12:05:42') def test_entry_1(self): uuid = 'af5304edb06c32131d502487eeb21009' self.assertEqual(self.passfile.records[uuid][2], 'Toplevel2') self.assertEqual(self.passfile.get_folder_list(uuid), ['Toplevel2']) self.assertEqual(self.passfile.records[uuid][3], 'Intoplevel2') self.assertEqual(self.passfile.records[uuid][4], 'username') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'password') self.assertFalse(8 in self.passfile.records[uuid]) self.assertFalse(12 in self.passfile.records[uuid]) def test_entry_2(self): uuid = '7f1f06b3a36c3213061dc476b88f83fd' self.assertFalse(2 in self.passfile.records[uuid]) self.assertEqual(self.passfile.get_folder_list(uuid), []) self.assertEqual(self.passfile.records[uuid][3], 'Toplevel') self.assertEqual(self.passfile.records[uuid][4], 'topuser') self.assertEqual(self.passfile.records[uuid][5], 'This is a note\n' + 'This is line 2\n' + 'Unicode: éléphant') self.assertEqual(self.passfile.records[uuid][6], 'toppass') self.assertFalse(8 in self.passfile.records[uuid]) self.assertFalse(12 in self.passfile.records[uuid]) def test_entry_3(self): uuid = '82bd036bb86c32130facf0a8e3e3809b' self.assertEqual(self.passfile.records[uuid][2], 'Top.withdot') self.assertEqual(self.passfile.get_folder_list(uuid), ['Top', 'withdot']) self.assertEqual(self.passfile.records[uuid][3], 'Indot') self.assertEqual(self.passfile.records[uuid][4], '') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'password') self.assertFalse(8 in self.passfile.records[uuid]) self.assertFalse(12 in self.passfile.records[uuid]) def test_entry_4(self): uuid = '116301c0aa6c321334769a2131e417c5' self.assertEqual(self.passfile.records[uuid][2], 'Toplevel1') self.assertEqual(self.passfile.get_folder_list(uuid), ['Toplevel1']) self.assertEqual(self.passfile.records[uuid][3], 'Intoplevel1') self.assertEqual(self.passfile.records[uuid][4], 'username') self.assertEqual(self.passfile.records[uuid][6], 'password') self.assertEqual(self.passfile.records[uuid][13], 'http://www.example.com') self.assertFalse(8 in self.passfile.records[uuid]) self.assertFalse(12 in self.passfile.records[uuid]) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612610.0 pasaffe-0.57/tests/test_pw_gorilla_15363.py0000644000175000017500000001247100000000000022205 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 class TestPasswordGorilla15363(unittest.TestCase): def setUp(self): self.passfile = PassSafeFile( './tests/databases/pw-gorilla-15363.psafe3', 'pasaffe') def test_num_entries(self): self.assertEqual(len(self.passfile.records), 4) def test_empty_folders(self): # This version of Password Gorilla doesn't save empty folders self.assertEqual(len(self.passfile.empty_folders), 0) def test_get_database_version_string(self): self.assertEqual(self.passfile.get_database_version_string(), "03.00") def test_get_database_uuid(self): self.assertEqual(self.passfile.header[1], b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00') def test_get_saved_name(self): self.assertEqual(self.passfile.get_saved_name(), None) def test_get_saved_host(self): self.assertEqual(self.passfile.get_saved_host(), None) def test_get_saved_application(self): self.assertEqual(self.passfile.get_saved_application(), None) def test_get_saved_date_string(self): self.assertEqual(self.passfile.get_saved_date_string(), None) def test_entry_1(self): uuid = '8642342dfd444e044f50ca8b3b12fd67' self.assertEqual(self.passfile.records[uuid][2], 'top\\.withdot') self.assertEqual(self.passfile.get_folder_list(uuid), ['top.withdot']) self.assertEqual(self.passfile.records[uuid][3], 'insidedot') self.assertEqual(self.passfile.records[uuid][4], 'usernamedot') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'passworddot') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Thu, 25 Jul 2013 23:58:56') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Thu, 25 Jul 2013 23:58:56') def test_entry_2(self): uuid = '666b02ae0f574d4549f8f57ddae70d77' self.assertFalse(2 in self.passfile.records[uuid]) self.assertEqual(self.passfile.get_folder_list(uuid), []) self.assertEqual(self.passfile.records[uuid][3], 'toplevel1') self.assertEqual(self.passfile.records[uuid][4], 'usernametop') self.assertEqual(self.passfile.records[uuid][5], 'This is a note') self.assertEqual(self.passfile.records[uuid][6], 'passwordtop') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Thu, 25 Jul 2013 23:58:06') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Thu, 25 Jul 2013 23:58:06') def test_entry_3(self): uuid = 'aff6f326353f4be86009bf342e0a0af7' self.assertEqual(self.passfile.records[uuid][2], 'topgroup1.topgroup2.topgroup3') self.assertEqual(self.passfile.get_folder_list(uuid), ['topgroup1', 'topgroup2', 'topgroup3']) self.assertEqual(self.passfile.records[uuid][3], 'entrylevel3') self.assertEqual(self.passfile.records[uuid][4], 'username3') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'password3') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Thu, 25 Jul 2013 00:33:33') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Thu, 25 Jul 2013 00:33:33') def test_entry_4(self): uuid = '66d1cf5e60dc424d6a3b19c89aed2f1e' self.assertEqual(self.passfile.records[uuid][2], 'topgroup1') self.assertEqual(self.passfile.get_folder_list(uuid), ['topgroup1']) self.assertEqual(self.passfile.records[uuid][3], 'entrylevel1') self.assertEqual(self.passfile.records[uuid][4], 'username1') self.assertEqual(self.passfile.records[uuid][5], 'This is a note\nThis is a second line\n' 'Unicode: éléphant') self.assertEqual(self.passfile.records[uuid][6], 'password1') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Thu, 25 Jul 2013 00:33:06') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Thu, 25 Jul 2013 00:33:06') if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612449.0 pasaffe-0.57/tests/test_pw_gorilla_1537.py0000644000175000017500000001530000000000000022115 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2014 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 class TestPasswordGorilla1537(unittest.TestCase): def setUp(self): self.passfile = PassSafeFile( './tests/databases/pw-gorilla-1537.psafe3', 'pasaffe') def test_num_entries(self): self.assertEqual(len(self.passfile.records), 6) def test_empty_folders(self): # This version of Password Gorilla doesn't save empty folders self.assertEqual(len(self.passfile.empty_folders), 0) def test_get_database_version_string(self): self.assertEqual(self.passfile.get_database_version_string(), "03.00") def test_get_database_uuid(self): self.assertEqual(self.passfile.header[1], b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00') def test_get_saved_name(self): self.assertEqual(self.passfile.get_saved_name(), None) def test_get_saved_host(self): self.assertEqual(self.passfile.get_saved_host(), None) def test_get_saved_application(self): self.assertEqual(self.passfile.get_saved_application(), None) def test_get_saved_date_string(self): self.assertEqual(self.passfile.get_saved_date_string(), None) def test_entry_1(self): uuid = '009eeceaad754732634e37d2f70b0985' self.assertEqual(self.passfile.records[uuid][2], 'top\\.withdot') self.assertEqual(self.passfile.get_folder_list(uuid), ['top.withdot']) self.assertEqual(self.passfile.records[uuid][3], 'insidedot') self.assertEqual(self.passfile.records[uuid][4], 'usernamedot') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'passworddot') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Sat, 25 Oct 2014 15:05:30') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Sat, 25 Oct 2014 15:05:30') def test_entry_2(self): uuid = 'd68cee64c2794300561837e248abddd1' self.assertFalse(2 in self.passfile.records[uuid]) self.assertEqual(self.passfile.get_folder_list(uuid), []) self.assertEqual(self.passfile.records[uuid][3], 'toplevel1') self.assertEqual(self.passfile.records[uuid][4], 'usernametop') self.assertEqual(self.passfile.records[uuid][5], 'This is a note') self.assertEqual(self.passfile.records[uuid][6], 'passwordtop') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Sat, 25 Oct 2014 15:04:48') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Sat, 25 Oct 2014 15:04:48') def test_entry_3(self): uuid = '8e869cc656a942fd7beecf36605140d8' self.assertEqual(self.passfile.records[uuid][2], 'topgroup1.topgroup2.topgroup3') self.assertEqual(self.passfile.get_folder_list(uuid), ['topgroup1', 'topgroup2', 'topgroup3']) self.assertEqual(self.passfile.records[uuid][3], 'entrylevel3') self.assertEqual(self.passfile.records[uuid][4], 'username3') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'password3') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Sat, 25 Oct 2014 15:06:51') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Sat, 25 Oct 2014 15:06:51') def test_entry_4(self): uuid = '3e317b83f4d645056abc5526f9c1f7e6' self.assertEqual(self.passfile.records[uuid][2], 'topgroup1') self.assertEqual(self.passfile.get_folder_list(uuid), ['topgroup1']) self.assertEqual(self.passfile.records[uuid][3], 'entrylevel1') self.assertEqual(self.passfile.records[uuid][4], 'username1') self.assertEqual(self.passfile.records[uuid][5], 'This is a note\nThis is a second line\n' 'Unicode: éléphant') self.assertEqual(self.passfile.records[uuid][6], 'password1') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Sat, 25 Oct 2014 15:07:24') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Sat, 25 Oct 2014 15:07:24') def test_entry_5(self): uuid = '11fbf64e83e240a25e8bb657cea6edcd' self.assertFalse(2 in self.passfile.records[uuid]) self.assertEqual(self.passfile.get_folder_list(uuid), []) self.assertEqual(self.passfile.records[uuid][3], 'topnopass') self.assertEqual(self.passfile.records[uuid][4], 'topusername') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], '') self.assertEqual(self.passfile.get_password_time(uuid, False), None) self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Sat, 25 Oct 2014 15:07:52') def test_entry_6(self): uuid = 'd0b7295a7fc447fa4d8c1aa701ba8c30' self.assertFalse(2 in self.passfile.records[uuid]) self.assertEqual(self.passfile.get_folder_list(uuid), []) self.assertEqual(self.passfile.records[uuid][3], 'topnouser') self.assertEqual(self.passfile.records[uuid][4], '') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'toppassword') self.assertEqual(self.passfile.get_password_time(uuid, False), 'Sat, 25 Oct 2014 15:07:40') self.assertEqual(self.passfile.get_modification_time(uuid, False), 'Sat, 25 Oct 2014 15:07:40') if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1572612674.0 pasaffe-0.57/tests/test_pwsafe_331.py0000644000175000017500000001217000000000000021154 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 class TestPasswordSafe331(unittest.TestCase): def setUp(self): self.passfile = PassSafeFile('./tests/databases/pwsafe-331.psafe3', 'pasaffe') def test_num_entries(self): self.assertEqual(len(self.passfile.records), 3) def test_empty_folders(self): empty_folders = [['emptygroup1'], ['emptygroup1', 'emptygroup2'], ['emptygroup1', 'emptygroup2', 'emptygroup3'], ['level1group', 'level2group'], ['emptygroup1', 'test'], ['emptygroup1', 'with/slash']] empty_fields = ['emptygroup1', 'emptygroup1.emptygroup2', 'emptygroup1.emptygroup2.emptygroup3', 'level1group.level2group', 'emptygroup1.test', 'emptygroup1.with/slash'] self.assertEqual(len(self.passfile.empty_folders), len(empty_fields)) self.assertEqual(self.passfile.get_empty_folders(), empty_folders) self.assertEqual(self.passfile.empty_folders, empty_fields) def test_get_database_version_string(self): self.assertEqual(self.passfile.get_database_version_string(), "03.0b") def test_get_database_uuid(self): self.assertEqual(self.passfile.header[1], b'cf\xfe\xea\x00wBU\xa2TM\xc3k\x0f>\x0f') def test_get_saved_name(self): self.assertEqual(self.passfile.get_saved_name(), "mdeslaur") def test_get_saved_host(self): self.assertEqual(self.passfile.get_saved_host(), "mdlinux") def test_get_saved_application(self): self.assertEqual(self.passfile.get_saved_application(), 'Password Safe V3.31') def test_get_saved_date_string(self): self.assertEqual(self.passfile.get_saved_date_string(False), 'Thu, 25 Jul 2013 23:57:08') def test_entry_1(self): uuid = '4a32a8ad616343b692e85c721bfce0e2' self.assertFalse(2 in self.passfile.records[uuid]) self.assertEqual(self.passfile.get_folder_list(uuid), []) self.assertEqual(self.passfile.records[uuid][3], 'topentry1') self.assertEqual(self.passfile.records[uuid][4], 'username1') self.assertEqual(self.passfile.records[uuid][5], 'This is a note\nThis is a second line\n' 'Unicode: éléphant') self.assertEqual(self.passfile.records[uuid][6], 'password1') self.assertEqual(self.passfile.get_creation_time(uuid, False), 'Thu, 25 Jul 2013 00:21:00') self.assertEqual(self.passfile.records[uuid][13], 'http://www.example.com') def test_entry_2(self): uuid = '722328d418584201803a119fa517b799' self.assertEqual(self.passfile.records[uuid][2], 'level1group') self.assertEqual(self.passfile.get_folder_list(uuid), ['level1group']) self.assertEqual(self.passfile.records[uuid][3], 'level1entry') self.assertEqual(self.passfile.records[uuid][4], 'username1') self.assertEqual(self.passfile.records[uuid][5], 'This is a note\n') self.assertEqual(self.passfile.records[uuid][6], 'password1') self.assertEqual(self.passfile.get_creation_time(uuid, False), 'Thu, 25 Jul 2013 00:25:42') def test_entry_3(self): uuid = 'cb16d230853247ad8cb12ef6ea615cb4' self.assertEqual(self.passfile.records[uuid][2], 'level1group.level2group.level3group') self.assertEqual(self.passfile.get_folder_list(uuid), ['level1group', 'level2group', 'level3group']) self.assertEqual(self.passfile.records[uuid][3], 'level3entry') self.assertEqual(self.passfile.records[uuid][4], 'usernamelevel3') self.assertFalse(5 in self.passfile.records[uuid]) self.assertEqual(self.passfile.records[uuid][6], 'passwordlevel3') self.assertEqual(self.passfile.get_creation_time(uuid, False), 'Thu, 25 Jul 2013 00:26:36') if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580230523.0 pasaffe-0.57/tests/test_readdb.py0000644000175000017500000006477000000000000020537 0ustar00mdeslaurmdeslaur00000000000000#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Copyright (C) 2013 Marc Deslauriers # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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 . # import sys import os.path import unittest import time from binascii import hexlify sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))) from pasaffe_lib.readdb import PassSafeFile # noqa: E402 class TestReadDB(unittest.TestCase): def setUp(self): self.passfile = PassSafeFile() def test_folder_list_to_field(self): folder_list = [[[], ""], [["foldera"], "foldera"], [["folder.a"], "folder\.a"], # noqa: W605 [["foldera."], "foldera\."], # noqa: W605 [[".foldera"], "\.foldera"], # noqa: W605 [["foldera.", "folderb."], "foldera\..folderb\."], # noqa: W605 [["foldera", "folderb"], "foldera.folderb"], [["folder.a", "folderb"], "folder\.a.folderb"], # noqa: W605 [["foldera", "folder.b"], "foldera.folder\.b"], [["folder.a", "folder.b"], "folder\.a.folder\.b"], [["folder.a", "folder.b", "folder.c"], "folder\.a.folder\.b.folder\.c"], [["", "foldera", "folderb"], ".foldera.folderb"], [["", "", "folderb"], "..folderb"], [["foldera", "", "folderb"], "foldera..folderb"], [["foldera", "folderb", ""], "foldera.folderb."], ] for (folder, field) in folder_list: self.assertEqual(self.passfile._folder_list_to_field(folder), field) def test_field_to_folder_list(self): folder_list = [["", []], ["foldera", ["foldera"]], ["folder\.a", ["folder.a"]], # noqa: W605 ["foldera\.", ["foldera."]], # noqa: W605 ["\.foldera", [".foldera"]], # noqa: W605 ["foldera\..folderb\.", ["foldera.", "folderb."]], # noqa: W605 ["foldera.folderb", ["foldera", "folderb"]], ["folder\.a.folderb", ["folder.a", "folderb"]], ["foldera.folder\.b", ["foldera", "folder.b"]], ["folder\.a.folder\.b", ["folder.a", "folder.b"]], ["folder\.a.folder\.b.folder\.c", ["folder.a", "folder.b", "folder.c"]], [".foldera.folderb", ["", "foldera", "folderb"]], ["..folderb", ["", "", "folderb"]], ["foldera..folderb", ["foldera", "", "folderb"]], ["foldera.folderb.", ["foldera", "folderb", ""]], ] for (field, folder) in folder_list: self.assertEqual(self.passfile._field_to_folder_list(field), folder) def test_get_database_version_string(self): self.passfile.new_db("test") expected = '%s.%s' % ( hexlify(self.passfile.db_version[1:2]).decode('utf-8'), hexlify(self.passfile.db_version[0:1]).decode('utf-8')) self.assertEqual(self.passfile.get_database_version_string(), expected) self.passfile.header[0] = b'\x0B\x03' self.assertEqual(self.passfile.get_database_version_string(), "03.0b") def test_new_entry(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() self.assertEqual(len(uuid_hex), 32) self.assertTrue(uuid_hex in self.passfile.records) for field in [1, 3, 4, 5, 6, 7, 8, 12, 13]: self.assertTrue(field in self.passfile.records[uuid_hex]) def test_delete_entry(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() self.assertTrue(uuid_hex in self.passfile.records) self.passfile.delete_entry(uuid_hex) self.assertTrue(uuid_hex not in self.passfile.records) def test_update_modification_time(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() old_time = self.passfile.records[uuid_hex][12] time.sleep(1.1) self.passfile.update_modification_time(uuid_hex) new_time = self.passfile.records[uuid_hex][12] self.assertTrue(old_time != new_time) def test_update_password_time(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() old_time = self.passfile.records[uuid_hex][8] time.sleep(1.1) self.passfile.update_password_time(uuid_hex) new_time = self.passfile.records[uuid_hex][8] self.assertTrue(old_time != new_time) def test_get_title(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() test_title = "Test title" self.passfile.records[uuid_hex][3] = test_title title = self.passfile.get_title(uuid_hex) self.assertTrue(title == test_title) def test_get_username(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() test_username = "awesomeuser" self.passfile.records[uuid_hex][4] = test_username username = self.passfile.get_username(uuid_hex) self.assertTrue(username == test_username) def test_get_notes(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() test_note = "This is a note." self.passfile.records[uuid_hex][5] = test_note note = self.passfile.get_notes(uuid_hex) self.assertTrue(note == test_note) def test_get_password(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() test_password = "s3cr3tp4ssw0rd" self.passfile.records[uuid_hex][6] = test_password password = self.passfile.get_password(uuid_hex) self.assertTrue(password == test_password) def test_get_url(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() test_url = "http://www.example.com" self.passfile.records[uuid_hex][13] = test_url url = self.passfile.get_url(uuid_hex) self.assertTrue(url == test_url) def test_update_folder_list(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() self.assertTrue(2 not in self.passfile.records[uuid_hex]) folder = ['folderA', 'folderB', 'folderC'] folder_field = 'folderA.folderB.folderC' self.passfile.update_folder_list(uuid_hex, folder) self.assertTrue(2 in self.passfile.records[uuid_hex]) self.assertTrue(self.passfile.records[uuid_hex][2] == folder_field) self.passfile.update_folder_list(uuid_hex, []) self.assertTrue(2 not in self.passfile.records[uuid_hex]) def test_get_folder_list(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() self.assertTrue(2 not in self.passfile.records[uuid_hex]) self.assertTrue(self.passfile.get_folder_list(uuid_hex) == []) folder = ['folderA', 'folderB', 'folderC'] folder_field = 'folderA.folderB.folderC' self.passfile.update_folder_list(uuid_hex, folder) self.assertTrue(2 in self.passfile.records[uuid_hex]) self.assertTrue(self.passfile.records[uuid_hex][2] == folder_field) self.assertTrue(self.passfile.get_folder_list(uuid_hex) == folder) self.assertTrue(self.passfile.get_folder_list('nonexistent') == []) def test_get_empty_folders(self): folder_fields = ['folderA', 'folderA.folderB', 'folderA.folderB.folderC'] folder_list = [['folderA'], ['folderA', 'folderB'], ['folderA', 'folderB', 'folderC']] self.assertTrue(self.passfile.get_empty_folders() == []) self.passfile.empty_folders = folder_fields self.assertTrue(self.passfile.get_empty_folders() == folder_list) def test_add_empty_folder(self): folder_fields = ['folderA', 'folderA.folderB', 'folderA.folderB.folderC'] folder = ['folderA', 'folderB', 'folderC'] # Make sure it's empty self.assertTrue(self.passfile.empty_folders == []) # Add a folder, and make sure it created the children self.passfile.add_empty_folder(folder) self.assertTrue(self.passfile.empty_folders == folder_fields) # Make sure empty parameters work self.passfile.add_empty_folder(None) self.assertTrue(self.passfile.empty_folders == folder_fields) self.passfile.add_empty_folder([]) self.assertTrue(self.passfile.empty_folders == folder_fields) # Try adding it again, to make sure we don't have duplicates self.passfile.add_empty_folder(folder) self.assertTrue(self.passfile.empty_folders == folder_fields) # Make sure adding an empty folder that isn't actually empty # works uuid_hex = self.passfile.new_entry() entry_folder = ['otherA'] self.passfile.update_folder_list(uuid_hex, entry_folder) self.passfile.add_empty_folder(entry_folder) self.assertTrue(self.passfile.empty_folders == folder_fields) # Adding an empty folder that only has an empty subfolder should # only add the subfolder uuid_hex = self.passfile.new_entry() entry_folder = ['thirdA', 'thirdB'] self.passfile.update_folder_list(uuid_hex, entry_folder) self.passfile.add_empty_folder(entry_folder) folder_fields.append('thirdA') self.assertTrue(self.passfile.empty_folders == folder_fields) # Do the same, but for 3 levels uuid_hex = self.passfile.new_entry() entry_folder = ['fourthA', 'fourthB', 'fourthC'] self.passfile.update_folder_list(uuid_hex, entry_folder) self.passfile.add_empty_folder(entry_folder) folder_fields.append('fourthA') folder_fields.append('fourthA.fourthB') self.assertTrue(self.passfile.empty_folders == folder_fields) def test_remove_empty_folder(self): folder_fields = ['folderA', 'folderA.folderB', 'folderA.folderB.folderC'] # pass by value self.passfile.empty_folders = folder_fields[:] # Try and remove a bogus folder self.passfile.remove_empty_folder(['bogus']) self.assertTrue(self.passfile.empty_folders == folder_fields) # Make sure empty parameters work self.passfile.remove_empty_folder(None) self.assertTrue(self.passfile.empty_folders == folder_fields) self.passfile.remove_empty_folder([]) self.assertTrue(self.passfile.empty_folders == folder_fields) # Now, remove an empty folder self.passfile.remove_empty_folder(['folderA', 'folderB']) folder_fields.remove('folderA.folderB') self.assertTrue(self.passfile.empty_folders == folder_fields) def test_get_all_folders(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() # First make sure there's no folders at all self.assertTrue(self.passfile.get_all_folders() == []) # Now handle an entry, but no empty folders folder = ['folderA', 'folderB'] self.passfile.update_folder_list(uuid_hex, folder) self.assertTrue(self.passfile.get_all_folders() == [folder]) # Now handle empty folders, but no entry self.passfile.update_folder_list(uuid_hex, []) self.assertTrue(self.passfile.get_all_folders() == []) folder_fields = ['folderA', 'folderA.folderB', 'folderA.folderB.folderC'] self.passfile.empty_folders = folder_fields folder_list = [['folderA'], ['folderA', 'folderB'], ['folderA', 'folderB', 'folderC']] self.assertTrue(self.passfile.get_all_folders() == folder_list) # Now handle multiple entries, and empty folders self.passfile.update_folder_list(uuid_hex, folder) folderB = ['OtherFolderA', 'OtherFolderB'] uuid_hex_B = self.passfile.new_entry() self.passfile.new_entry() self.passfile.update_folder_list(uuid_hex_B, folderB) all_folders = folder_list + [folderB] self.assertTrue(self.passfile.get_all_folders() == all_folders) def test_rename_folder_list(self): self.passfile.new_db("test") # Create a few entries folderA = ['firstA', 'firstB'] folderB = ['firstA', 'firstB', 'firstC'] folderC = ['secondA', 'secondB'] uuid_hex_A = self.passfile.new_entry() uuid_hex_B = self.passfile.new_entry() uuid_hex_C = self.passfile.new_entry() uuid_hex_D = self.passfile.new_entry() self.passfile.update_folder_list(uuid_hex_A, folderA) self.passfile.update_folder_list(uuid_hex_B, folderB) self.passfile.update_folder_list(uuid_hex_C, folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_A) == folderA) self.assertTrue(self.passfile.get_folder_list(uuid_hex_B) == folderB) self.assertTrue(self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_D) == []) # Ok, let's rename one new_folderA = ['firstA', 'renamedB'] new_folderB = ['firstA', 'renamedB', 'firstC'] self.passfile.rename_folder_list(folderA, new_folderA) self.assertTrue( self.passfile.get_folder_list(uuid_hex_A) == new_folderA) self.assertTrue( self.passfile.get_folder_list(uuid_hex_B) == new_folderB) self.assertTrue( self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue( self.passfile.get_folder_list(uuid_hex_D) == []) # Now add some empty_folders folder_fields = ['firstA', 'secondA', 'thirdA', 'thirdA.thirdB'] # pass by value self.passfile.empty_folders = folder_fields[:] # OK, rename entries and empty_folders another_new_folderA = ['renamedA', 'renamedB'] another_new_folderB = ['renamedA', 'renamedB', 'firstC'] new_empty_folders = [['secondA'], ['thirdA'], ['thirdA', 'thirdB'], ['renamedA']] self.passfile.rename_folder_list(['firstA'], ['renamedA']) self.assertTrue( self.passfile.get_folder_list(uuid_hex_A) == another_new_folderA) self.assertTrue( self.passfile.get_folder_list(uuid_hex_B) == another_new_folderB) self.assertTrue( self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue( self.passfile.get_folder_list(uuid_hex_D) == []) self.assertTrue( self.passfile.get_empty_folders() == new_empty_folders) def test_delete_folder(self): self.passfile.new_db("test") # Create a few entries folderA = ['firstA', 'firstB'] folderB = ['firstA', 'firstB', 'firstC'] folderC = ['secondA', 'secondB'] uuid_hex_A = self.passfile.new_entry() uuid_hex_B = self.passfile.new_entry() uuid_hex_C = self.passfile.new_entry() uuid_hex_D = self.passfile.new_entry() self.passfile.update_folder_list(uuid_hex_A, folderA) self.passfile.update_folder_list(uuid_hex_B, folderB) self.passfile.update_folder_list(uuid_hex_C, folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_A) == folderA) self.assertTrue(self.passfile.get_folder_list(uuid_hex_B) == folderB) self.assertTrue(self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_D) == []) self.assertTrue(self.passfile.get_empty_folders() == []) # Try and delete some invalid things self.passfile.delete_folder(None) self.passfile.delete_folder([]) self.passfile.delete_folder(['banana']) self.assertTrue(self.passfile.get_folder_list(uuid_hex_A) == folderA) self.assertTrue(self.passfile.get_folder_list(uuid_hex_B) == folderB) self.assertTrue(self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_D) == []) self.assertTrue(self.passfile.get_empty_folders() == []) # Now, delete a third level. self.passfile.delete_folder(folderB) self.assertTrue(self.passfile.get_folder_list(uuid_hex_A) == folderA) self.assertTrue(uuid_hex_B not in self.passfile.records) self.assertTrue(self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_D) == []) self.assertTrue(uuid_hex_D in self.passfile.records) self.assertTrue(self.passfile.get_empty_folders() == []) # Add some empty_folders and delete a toplevel emptyA = [['firstA'], ['firstA', 'firstB', 'emptyC']] emptyB = emptyA + [['some'], ['some', 'random']] for folder in emptyB: self.passfile.add_empty_folder(folder) self.assertTrue(self.passfile.get_empty_folders() == emptyB) self.passfile.delete_folder(['some']) self.assertTrue(self.passfile.get_empty_folders() == emptyA) self.assertTrue(self.passfile.get_folder_list(uuid_hex_A) == folderA) self.assertTrue(self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_D) == []) self.assertTrue(uuid_hex_D in self.passfile.records) # Now delete a top level self.passfile.delete_folder(['firstA']) self.assertTrue(self.passfile.get_empty_folders() == []) self.assertTrue(uuid_hex_A not in self.passfile.records) self.assertTrue(self.passfile.get_folder_list(uuid_hex_C) == folderC) self.assertTrue(self.passfile.get_folder_list(uuid_hex_D) == []) self.assertTrue(uuid_hex_D in self.passfile.records) # Delete the remaining second level. We should gain an empty folder. self.passfile.delete_folder(['secondA', 'secondB']) self.assertTrue(uuid_hex_C not in self.passfile.records) self.assertTrue(self.passfile.get_folder_list(uuid_hex_D) == []) self.assertTrue(uuid_hex_D in self.passfile.records) self.assertTrue(self.passfile.get_empty_folders() == [['secondA']]) def test_get_tree_status(self): self.passfile.new_db("test") self.assertEqual(self.passfile.get_tree_status(), None) self.passfile.header[6] = "Pasaffe test" self.assertEqual(self.passfile.get_tree_status(), None) self.passfile.header[3] = "101010" self.assertEqual(self.passfile.get_tree_status(), "101010") self.passfile.header[6] = "AnotherApp v1" self.assertEqual(self.passfile.get_tree_status(), None) def test_set_tree_status(self): self.passfile.new_db("test") self.passfile.header[6] = "Pasaffe test" self.assertEqual(self.passfile.get_tree_status(), None) self.passfile.set_tree_status("101010") self.assertEqual(self.passfile.get_tree_status(), "101010") self.assertTrue(3 in self.passfile.header) self.passfile.set_tree_status(None) self.assertEqual(self.passfile.get_tree_status(), None) self.assertFalse(3 in self.passfile.header) def test_fixups(self): self.passfile.new_db("test") uuid_hex = self.passfile.new_entry() # New entry should have empty strings self.assertEqual(self.passfile.records[uuid_hex][3], '') self.assertEqual(self.passfile.records[uuid_hex][4], '') self.assertEqual(self.passfile.records[uuid_hex][5], '') self.assertEqual(self.passfile.records[uuid_hex][6], '') # Delete title, username and password del self.passfile.records[uuid_hex][3] del self.passfile.records[uuid_hex][4] del self.passfile.records[uuid_hex][6] # add a comment with CRLF terminators crlf = ("First line\r\n" + "Second line\r\n" + "Third line") lf = ("First line\n" + "Second line\n" + "Third line") self.passfile.records[uuid_hex][5] = crlf self.assertTrue(3 not in self.passfile.records[uuid_hex]) self.assertTrue(4 not in self.passfile.records[uuid_hex]) self.assertTrue(6 not in self.passfile.records[uuid_hex]) self.assertEqual(self.passfile.records[uuid_hex][5], crlf) # Now do the postread fixup self.passfile._postread_fixup() self.assertEqual(self.passfile.records[uuid_hex][3], '') self.assertEqual(self.passfile.records[uuid_hex][4], '') self.assertEqual(self.passfile.records[uuid_hex][5], lf) self.assertEqual(self.passfile.records[uuid_hex][6], '') # OK, do the presave fixup self.assertEqual(self.passfile._presave_fixup(uuid_hex, 5), crlf) def _create_find_db(self): entries = [{3: "carte de crédit", 4: "username1", 5: "This is a note", 6: "password1", 13: "http://www.example.com"}, {2: "folder1", 3: "carte de credit", 4: "username1", 5: "anothernote", 6: "password1"}, {2: "level1group.level2group.level3group", 3: "level3entry", 4: "usernamelevel3", 6: "passwordlevel3", 13: "http://note.com"}] self.passfile.new_db('pasaffe') for entry in entries: uuid = self.passfile.new_entry() for key in entry.keys(): self.passfile.records[uuid][key] = entry[key] def test_update_find_results(self): self._create_find_db() self.passfile.update_find_results("") self.assertEqual(self.passfile.find_results, []) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "") # Test field 3 self.passfile.update_find_results("level3entry") self.assertEqual(len(self.passfile.find_results), 1) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "level3entry") # Test field 5 self.passfile.update_find_results("another") self.assertEqual(len(self.passfile.find_results), 1) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "another") # Test field 13 self.passfile.update_find_results("example.com") self.assertEqual(len(self.passfile.find_results), 1) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "example.com") # Test matching entries with accents self.passfile.update_find_results("credit") self.assertEqual(len(self.passfile.find_results), 2) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "credit") # Test specifying search term with accent self.passfile.update_find_results("crédit") self.assertEqual(len(self.passfile.find_results), 2) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "crédit") # Test force option self.passfile.find_results = [] self.passfile.update_find_results("crédit") self.assertEqual(len(self.passfile.find_results), 0) self.passfile.update_find_results("crédit", True) self.assertEqual(len(self.passfile.find_results), 2) # Test clearing out values self.passfile.update_find_results("") self.assertEqual(self.passfile.find_results, []) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "") def test_get_next_find_result(self): self._create_find_db() self.assertEqual(self.passfile.get_next_find_result(), None) self.assertEqual(self.passfile.get_next_find_result(True), None) self.passfile.update_find_results("note") self.assertEqual(len(self.passfile.find_results), 3) self.assertEqual(self.passfile.find_results_index, None) self.assertEqual(self.passfile.find_value, "note") self.assertEqual(self.passfile.find_results_index, None) uuid1 = self.passfile.get_next_find_result() self.assertEqual(self.passfile.find_results_index, 0) self.assertNotEqual(uuid1, None) uuid2 = self.passfile.get_next_find_result() self.assertEqual(self.passfile.find_results_index, 1) self.assertNotEqual(uuid2, None) self.assertNotEqual(uuid1, uuid2) uuid3 = self.passfile.get_next_find_result() self.assertEqual(self.passfile.find_results_index, 2) self.assertNotEqual(uuid3, None) self.assertNotEqual(uuid1, uuid3) self.assertNotEqual(uuid2, uuid3) # This should wrap around uuid4 = self.passfile.get_next_find_result() self.assertEqual(self.passfile.find_results_index, 0) self.assertNotEqual(uuid4, None) self.assertEqual(uuid1, uuid4) # And now try backwards uuid5 = self.passfile.get_next_find_result(True) self.assertEqual(self.passfile.find_results_index, 2) self.assertNotEqual(uuid5, None) self.assertEqual(uuid3, uuid5) if __name__ == '__main__': unittest.main()