├── .editorconfig ├── LICENSE ├── Makefile ├── README.md ├── addpass ├── config.example ├── keepass2import.py ├── pass2csv.py ├── rofi-pass └── screenshot.jpg /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | 9 | indent_style = tab 10 | 11 | [Makefile] 12 | indent_style = tab 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [*.py] 18 | indent_style = space 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifndef PREFIX 2 | PREFIX=/usr/local 3 | endif 4 | 5 | install: 6 | install -Dm755 rofi-pass $(DESTDIR)$(PREFIX)/bin/rofi-pass 7 | install -Dm755 addpass $(DESTDIR)$(PREFIX)/bin/addpass 8 | install -Dm644 config.example $(DESTDIR)$(PREFIX)/share/doc/rofi-pass/config.example 9 | install -Dm644 README.md $(DESTDIR)$(PREFIX)/share/doc/rofi-pass/README.md 10 | install -Dm644 config.example $(DESTDIR)/etc/rofi-pass.conf 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # !!! This script is unmaintained !!! 2 | 3 | # rofi-pass 4 | 5 | A bash script to handle [Simple Password Store](http://www.passwordstore.org/) 6 | in a convenient way using [rofi](https://github.com/DaveDavenport/rofi). 7 | 8 | ![rofi-pass](https://53280.de/rofi/rofi-pass.png "rofi-pass in action") 9 | 10 | ## Features 11 | 12 | * Open URLs of entries with hotkey 13 | * Add new Entries to Password Storage 14 | * Edit existing Entries 15 | * Generate new passwords for entries 16 | * Inline view, which can copy/type individual entries 17 | * Move/Delete existing entries 18 | * Support for different password stores (roots), e.g. to separate passwords for 19 | work from private passwords 20 | * Type any field from entry 21 | * Auto-typing of user and/or password fields. 22 | The format for password files should look like: 23 | 24 | ``` 25 | foobarmysecurepassword 26 | user: MyUser 27 | url: http://my.url.foo 28 | ``` 29 | 30 | * Auto-typing username based on path. 31 | The structure of your password store must be like: 32 | 33 | ``` 34 | foo/bar/site.com/username 35 | ``` 36 | 37 | And you must set the `default-autotype` to `'path :tab pass'`. 38 | 39 | * Auto-typing of more than one field, using the `autotype` entry: 40 | 41 | ``` 42 | foobarmysecurepassword 43 | --- 44 | user: MyUser 45 | SomeField: foobar 46 | AnotherField: barfoo 47 | url: http://my.url.foo 48 | autotype: SomeField :tab user :tab AnotherField :tab pass 49 | ``` 50 | 51 | You can use `:tab`, `:enter`, or `:space` here to type Tab, 52 | Enter, or Space (useful for toggling checkboxes) 53 | respectively. 54 | `:otp` will generate an OTP, either `pass-otp(1)` style, or according to the 55 | `otp_method:`, if it is defined. 56 | * Generating OTPs. 57 | The format for OTPs should either compatible with `pass-otp(1)`: 58 | 59 | ``` 60 | [...] 61 | otpauth://[...] 62 | ``` 63 | 64 | Or it should define a method for generating OTPs: 65 | 66 | ``` 67 | [...] 68 | otp_method: /opt/obscure-otp-generator/oog --some-option some args 69 | ``` 70 | 71 | `:delay` will trigger a delay (2 seconds by default). 72 | * All hotkeys are configurable in the config file 73 | * The field names for `user`, `url` and `autotype` are configurable 74 | * Bookmarks mode (open stored URLs in browser, default: Alt+x) 75 | * Share common used passwords between several entries (with different URLs, usernames etc) 76 | * Change backend with environment variable `ROFI_PASS_BACKEND`, valid 77 | backends are `xdotool` or `wtype`. For example use `rofi-pass` with 78 | [wtype](https://github.com/atx/wtype): 79 | 80 | ``` 81 | ROFI_PASS_BACKEND=wtype rofi-pass 82 | ``` 83 | 84 | or 85 | 86 | ``` 87 | ROFI_PASS_BACKEND=wtype ROFI_PASS_CLIPBOARD_BACKEND=wl-clipboard rofi-pass 88 | ``` 89 | 90 | Alternative change the backend in the config file using 91 | `backend=wtype` or `clibpoard_backend=wl-clipboard`. 92 | ## Requirements 93 | 94 | * [pass](http://www.passwordstore.org/) 95 | * sed 96 | * [rofi](https://github.com/DaveDavenport/rofi) 97 | * xdotool or wtype 98 | * xclip or wl-clipboard 99 | * gawk 100 | * bash 4.x 101 | * find 102 | * pwgen 103 | * [pass-otp](https://github.com/tadfisher/pass-otp) (optional: for OTPs) 104 | 105 | ### BSD 106 | 107 | * gnugrep 108 | * gawk 109 | 110 | ## Configuration 111 | 112 | rofi-pass may read its configuration values from different locations in the following order: 113 | * `ROFI_PASS_CONFIG` (environment variable) 114 | * `$HOME/.config/rofi-pass/config` 115 | * `/etc/rofi-pass.conf` 116 | 117 | rofi-pass only loads the first existing file. 118 | In case no config file exists, rofi-pass uses its internal default values. 119 | You can set the environment variable like this: 120 | 121 | ``` 122 | ROFI_PASS_CONFIG="$HOME/path/to/config" rofi-pass 123 | ``` 124 | 125 | For an example configuration please take a look at the included `config.example` file. 126 | 127 | ## Extras 128 | 129 | ### addpass 130 | 131 | rofi-pass comes with a tiny helper script, which makes it easier to create new pass entries. 132 | Just run it with 133 | 134 | ``` 135 | addpass --name "My new Site" +user "zeltak" +branch "branch" +custom "foobar" +autotype "branch :tab user :tab pass" 136 | ``` 137 | 138 | * First argument `--name` is mandatory. This will be the filename of the new password entry. 139 | * Second argument can be `--root` followed by absolute path to your password-store. addpass also uses root config setting from rofi-pass config file. If both are not found, PASSWORD_STORE_DIR variable is checked. If none of the above are found, the default location `$HOME/.password-store` is used. 140 | * `--root` can also be a colon separated list of directories, in which case you can navigate between them on the main menu with Shift+Left and Shift+Right. 141 | * Fieldnames are defined with `+` and the actual value is defined inside the quotations. You can add as many fields as you like 142 | 143 | ### keepass2 import script 144 | 145 | Also included is an import script for keepass2 databases. It's the same script that can be downloaded from the pass homepage, with some minor modifications to match rofi-pass structure. 146 | 147 | ### csv exporter 148 | 149 | Finally a script to export your pass database to csv is included. The resulting csv was tested in keepassxc. 150 | 151 | ## Sharing passwords 152 | 153 | Rofi-pass allows you to easily share common used passwords across multiple entries. 154 | For example, if you have an academic account which includes several services (such as a library, Salary, Student portal etc), all with different URL's, login forms etc. you can share one password across all of them. This is very handy when the passwords change annually. 155 | To use this function you need to add the following line instead of the password, referencing a pass file which holds the password: 156 | 157 | ``` 158 | #FILE=PATH/to/filename 159 | ``` 160 | 161 | where PATH is relative to your password-store. 162 | 163 | *And yes, you should slap your service provider for forcing you to share passwords across services.* 164 | 165 | ## User filename as user 166 | 167 | If your password file has no user field you can ask rofi-pass to use the filename instead. 168 | For example with this password file path : `web/fsf.org/rms` rofi-pass will user `rms` as your username. 169 | To get this, you need to set `default_user` to `:filename` in your configuration. 170 | 171 | ## FAQ 172 | 173 | ### rofi pass prints garbage instead of my actual passes 174 | 175 | Make sure to run `setxkbmap ` at the start of your Xorg 176 | session. 177 | 178 | ### rofi pass hangs after selecting password 179 | 180 | To access passwords your GPG agent needs to have unlocked your secret key 181 | you're using to encrypt your passwords with. If the secret key hasn't been 182 | unlocked yet it will prompt for the secret key password using `pinentry`. If 183 | `pinentry` is configured to read from a `tty` then `rofi-pass` will hang 184 | indefinitely. To fix this you need to [configure gpg][gpg_pinentry_config] to 185 | use a gui version of `pinentry`. E.g. 186 | ``` 187 | $ cat ~/.gnupg/gpg-agent.conf 188 | pinentry-program /usr/bin/pinentry-qt 189 | ``` 190 | You may have to kill the GPG agent if it's still in a bad state: 191 | ``` 192 | killall -9 gpg-agent 193 | ``` 194 | 195 | ## Alternative 196 | 197 | jreinert has written the roughly compatible tool 198 | [autopass](https://github.com/jreinert/autopass). It has less features, but 199 | definately saner code. 200 | Also he provided a nice little script called `passed` to change your 201 | fieldnames. [link](https://github.com/jreinert/passed) 202 | 203 | [gpg_pinentry_config][https://github.com/bfrg/gpg-guide/blob/master/gpg-agent.conf#L15] 204 | -------------------------------------------------------------------------------- /addpass: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source $HOME/.config/rofi-pass/config 4 | 5 | if [[ -n "$3" && "$2" == "--root" ]]; then 6 | root="${2}" 7 | elif [[ -n $root ]]; then 8 | root=$root 9 | elif [[ -n $PASSWORD_STORE_DIR ]]; then 10 | root=$PASSWORD_STORE_DIR 11 | else 12 | root="$HOME/.password-store" 13 | fi 14 | 15 | if [[ $1 == "--help" || $1 == "-h" ]]; then 16 | echo "add pass files for rofi-pass" 17 | echo "(C) 2015 Rasmus Steinke " 18 | echo "" 19 | echo "--name \"foobar\" Mandatory first argument - filename of password file" 20 | echo "--root \"foobar\" Optional second argument - Absolute path to password store" 21 | echo "" 22 | echo "+FIELD \"barbaz\" Every field name has to start with \"+\"" 23 | echo " Values should be quoted" 24 | echo "" 25 | echo "Example:" 26 | echo "addpass --name \"my password file\" --root \"$HOME/passwords\" +user \"Richard\" +foo \"bar\" +autotype \"foo :tab user :tab pass\"" 27 | exit 28 | else 29 | echo "$1" 30 | if [[ $1 != "--name" ]]; then 31 | echo "Missing --name option. Try --help" 32 | exit 33 | elif [[ $1 == "--name" ]]; then 34 | Name="$2" 35 | fi 36 | fi 37 | 38 | echo "Using database \"$root\"" 39 | 40 | OIFS=$IFS; 41 | IFS="+"; 42 | 43 | fields="$@"; 44 | fieldsArray=($fields); 45 | read -p "Enter password for entry \"${Name}\" > " -s pass 46 | 47 | cd "${root}" 48 | group=$(echo -e "No Group\n---\n$(find -type d -not -iwholename '*.git*' -printf '%d\t%P\n' | sort -r -nk1 | cut -f2-)" | rofi -dmenu -p "Choose Group > ") 49 | 50 | echo -e "\n\nStoring file ${Name} in group ${group}" 51 | 52 | printEntry () { 53 | echo -e "$pass\n---" 54 | for ((i=1; i<${#fieldsArray[@]}; ++i)); do 55 | field=$(echo "${fieldsArray[$i]}" | awk -F' ' '{print $1}') 56 | option=$(echo "${fieldsArray[$i]}" | cut -d ' ' -f 2- | sed -e 's/[[:blank:]]\+$//') 57 | echo "$field: $option" | grep -Ev 'name:|--root|root:|^:' #${fieldsArray[$i]}"; 58 | done 59 | } 60 | 61 | if [[ "$group" == "No Group" ]]; then 62 | printEntry | PASSWORD_STORE_DIR="${root}" pass insert -m "${Name}" 63 | elif [[ "$group" == "" ]]; then 64 | exit 65 | else 66 | printEntry | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${Name}" 67 | fi 68 | -------------------------------------------------------------------------------- /config.example: -------------------------------------------------------------------------------- 1 | # permanently set alternative root dir. Use ":" to separate multiple roots 2 | # which can be switched at runtime with shift+left/right 3 | # root=/path/to/root 4 | 5 | # rofi command. Make sure to have "$@" as last argument 6 | _rofi () { 7 | rofi -i -no-auto-select "$@" 8 | } 9 | 10 | # default command to generate passwords 11 | _pwgen () { 12 | pwgen -y "$@" 13 | } 14 | 15 | # image viewer to display qrcode of selected entry 16 | # qrencode is needed to generate the image and a viewer 17 | # that can read from pipes. Known viewers to work are feh and display 18 | _image_viewer () { 19 | feh - 20 | # display 21 | } 22 | 23 | # xdotool needs the keyboard layout to be set using setxkbmap 24 | # You can do this in your autostart scripts (e.g. xinitrc) 25 | 26 | # If for some reason, you cannot do this, you can set the command here. 27 | # and set fix_layout to true 28 | fix_layout=false 29 | 30 | layout_cmd () { 31 | setxkbmap us 32 | } 33 | 34 | # fields to be used 35 | URL_field='url' 36 | USERNAME_field='user' 37 | AUTOTYPE_field='autotype' 38 | 39 | # delay to be used for :delay keyword 40 | delay=2 41 | 42 | # rofi-pass needs to close itself before it can type passwords. Set delay here. 43 | wait=0.2 44 | 45 | # delay between keypresses when typing (in ms) 46 | type_delay=12 47 | 48 | ## Programs to be used 49 | # Editor 50 | EDITOR='gvim -f' 51 | 52 | # Browser 53 | BROWSER='xdg-open' 54 | 55 | ## Misc settings 56 | 57 | default_do='menu' # menu, autotype, copyPass, typeUser, typePass, copyUser, copyUrl, viewEntry, typeMenu, actionMenu, copyMenu, openUrl 58 | auto_enter='false' 59 | notify='false' 60 | default_autotype='user :tab pass' 61 | 62 | # color of the help messages 63 | # leave empty for autodetection 64 | help_color="#4872FF" 65 | 66 | # Clipboard settings 67 | # Possible options: primary, clipboard, both 68 | clip=primary 69 | 70 | # Seconds before clearing pass from clipboard 71 | clip_clear=45 72 | 73 | ## Options for generating new password entries 74 | 75 | # open new password entries in editor 76 | edit_new_pass="true" 77 | 78 | # default_user is also used for password files that have no user field. 79 | #default_user="${ROFI_PASS_DEFAULT_USER-$(whoami)}" 80 | #default_user2=mary_ann 81 | #password_length=12 82 | 83 | # Custom Keybindings 84 | autotype="Alt+1" 85 | type_user="Alt+2" 86 | type_pass="Alt+3" 87 | open_url="Alt+4" 88 | copy_name="Alt+u" 89 | copy_url="Alt+l" 90 | copy_pass="Alt+p" 91 | show="Alt+o" 92 | copy_entry="Alt+2" 93 | type_entry="Alt+1" 94 | copy_menu="Alt+c" 95 | action_menu="Alt+a" 96 | type_menu="Alt+t" 97 | help="Alt+h" 98 | switch="Alt+x" 99 | insert_pass="Alt+n" 100 | 101 | # Change the clipboard backend for rofi-pass, valid backends are: 102 | # xclip 103 | # wl-clipboard 104 | #clibpoard_backend=xclip 105 | 106 | # Change the backend for rofi-pass, valid backends are: 107 | # xdotool 108 | # wtype 109 | #backend=xdotool 110 | -------------------------------------------------------------------------------- /keepass2import.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2013 Stefan Simroth . All Rights Reserved. 5 | # Based on the script for KeepassX by Juhamatti Niemelä . 6 | # This file is licensed under the GPLv2+. Please see COPYING for more information. 7 | # 8 | # Usage: 9 | # ./keepass2pass.py -f export.xml 10 | # By default, takes the name of the root element and puts all passwords in there, but you can disable this: 11 | # ./keepass2pass.py -f export.xml -r "" 12 | # Or you can use another root folder: 13 | # ./keepass2pass.py -f export.xml -r foo 14 | # 15 | # Features: 16 | # * This script can handle duplicates and will merge them. 17 | # * Besides the password also the fields 'UserName', 'URL' and 'Notes' (comment) will be inserted. 18 | # * You get a warning if an entry has no password, but it will still insert it. 19 | 20 | # Minor Modifications by Rasmus Steinke 21 | 22 | import getopt, sys 23 | from subprocess import Popen, PIPE 24 | from xml.etree import ElementTree 25 | 26 | 27 | def pass_import_entry(path, data): 28 | """ Import new password entry to password-store using pass insert command """ 29 | proc = Popen(['pass', 'insert', '--multiline', path], stdin=PIPE, stdout=PIPE) 30 | proc.communicate(data.encode('utf8')) 31 | proc.wait() 32 | 33 | 34 | def get_value(elements, node_text): 35 | for element in elements: 36 | for child in element.findall('Key'): 37 | if child.text == node_text: 38 | return element.find('Value').text 39 | return '' 40 | 41 | def path_for(element, path=''): 42 | """ Generate path name from elements title and current path """ 43 | if element.tag == 'Entry': 44 | title = get_value(element.findall("String"), "Title") 45 | elif element.tag == 'Group': 46 | title = element.find('Name').text 47 | else: title = '' 48 | 49 | if path == '': return title 50 | else: return '/'.join([path, title]) 51 | 52 | def password_data(element, path=''): 53 | """ Return password data and additional info if available from password entry element. """ 54 | data = "" 55 | password = get_value(element.findall('String'), 'Password') 56 | if password is not None: data = password + "\n" 57 | else: 58 | print "[WARN] No password: %s" % path_for(element, path) 59 | 60 | username = get_value(element, 'UserName') 61 | url = get_value(element, 'URL') 62 | notes = get_value(element, 'Notes') 63 | data = "%suser: %s\n" % (data, username) 64 | data = "%surl: %s\n" % (data, url) 65 | data = "%s%s\n" % (data, notes) 66 | return data 67 | 68 | def import_entry(entries, element, path=''): 69 | element_path = path_for(element, path) 70 | if entries.has_key(element_path): 71 | existing_data = entries[element_path] 72 | data = (existing_data) 73 | else: 74 | data = password_data(element, path) 75 | entries[element_path] = data 76 | 77 | def import_group(entries, element, path=''): 78 | """ Import all entries and sub-groups from given group """ 79 | npath = path_for(element, path) 80 | for group in element.findall('Group'): 81 | import_group(entries, group, npath) 82 | for entry in element.findall('Entry'): 83 | import_entry(entries, entry, npath) 84 | 85 | def import_passwords(xml_file, root_path=None): 86 | """ Parse given Keepass2 XML file and import password groups from it """ 87 | print "[>>>>] Importing passwords from file %s" % xml_file 88 | print "[INFO] Root path: %s" % root_path 89 | entries = dict() 90 | with open(xml_file) as xml: 91 | text = xml.read() 92 | xml_tree = ElementTree.XML(text) 93 | root = xml_tree.find('Root') 94 | root_group = root.find('Group') 95 | import_group(entries,root_group,'') 96 | if root_path is None: root_path = root_group.find('Name').text 97 | groups = root_group.findall('Group') 98 | for group in groups: 99 | import_group(entries, group, root_path) 100 | password_count = 0 101 | for path, data in sorted(entries.iteritems()): 102 | sys.stdout.write("[>>>>] Importing %s ... " % path.encode("utf-8")) 103 | pass_import_entry(path, data) 104 | sys.stdout.write("OK\n") 105 | password_count += 1 106 | 107 | print "[ OK ] Done. Imported %i passwords." % password_count 108 | 109 | 110 | def usage(): 111 | """ Print usage """ 112 | print "Usage: %s -f XML_FILE" % (sys.argv[0]) 113 | print "Optional:" 114 | print " -r ROOT_PATH Different root path to use than the one in xml file, use \"\" for none" 115 | 116 | 117 | def main(argv): 118 | try: 119 | opts, args = getopt.gnu_getopt(argv, "f:r:") 120 | except getopt.GetoptError as err: 121 | print str(err) 122 | usage() 123 | sys.exit(2) 124 | 125 | xml_file = None 126 | root_path = None 127 | 128 | for opt, arg in opts: 129 | if opt in "-f": 130 | xml_file = arg 131 | if opt in "-r": 132 | root_path = arg 133 | 134 | if xml_file is not None: 135 | import_passwords(xml_file, root_path) 136 | else: 137 | usage() 138 | sys.exit(2) 139 | 140 | if __name__ == '__main__': 141 | main(sys.argv[1:]) 142 | -------------------------------------------------------------------------------- /pass2csv.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | import sys 4 | import gnupg 5 | 6 | ## Usage 7 | # Run pass2csv /path/to/password-storage 8 | 9 | ## options 10 | # field names 11 | userfield = "user" 12 | urlfield = "url" 13 | 14 | def traverse(path): 15 | for root, dirs, files in os.walk(path): 16 | if '.git' in dirs: 17 | dirs.remove('.git') 18 | for name in files: 19 | yield os.path.join(root, name) 20 | 21 | 22 | def parse(basepath, path, data): 23 | name = os.path.splitext(os.path.basename(path))[0] 24 | group = os.path.dirname(os.path.os.path.relpath(path, basepath)) 25 | split_data = data.split('\n') 26 | password = split_data[0] 27 | matching_user = [s for s in split_data if userfield+": " in s] 28 | user = None 29 | url = None 30 | if matching_user: 31 | for x in matching_user: 32 | user_split = x.split(userfield+": ") 33 | if len(user_split) == 2: 34 | user = user_split[1] 35 | else: 36 | user = None 37 | matching_url = [s for s in split_data if urlfield+": " in s] 38 | if matching_url: 39 | for x in matching_url: 40 | url_split = x.split(urlfield+": ") 41 | if len(url_split) == 2: 42 | url = url_split[1] 43 | else: 44 | url = None 45 | if url == "None": 46 | if user == "None": 47 | return [group, name, password] 48 | else: 49 | return [group, name, password, user] 50 | else: 51 | if user == "None": 52 | return [group, name, password, url] 53 | else: 54 | return [group, name, password, url, user] 55 | 56 | 57 | 58 | def main(path): 59 | gpg = gnupg.GPG() 60 | gpg.encoding = 'utf-8' 61 | csv_data = [] 62 | for file_path in traverse(path): 63 | if os.path.splitext(file_path)[1] == '.gpg': 64 | with open(file_path, 'rb') as f: 65 | data = str(gpg.decrypt_file(f)) 66 | csv_data.append(parse(path, file_path, data)) 67 | 68 | with open('pass.csv', 'w', newline='') as csv_file: 69 | writer = csv.writer(csv_file, delimiter=',') 70 | writer.writerows(csv_data) 71 | 72 | 73 | if __name__ == '__main__': 74 | path = os.path.abspath(sys.argv[1]) 75 | main(path) 76 | -------------------------------------------------------------------------------- /rofi-pass: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # rofi-pass 4 | # (c) 2015 Rasmus Steinke 5 | basecommand="$0" 6 | 7 | # set default settings 8 | _rofi () { 9 | rofi -no-auto-select -i "$@" 10 | } 11 | 12 | _pwgen () { 13 | pwgen -y "$@" 14 | } 15 | 16 | _image_viewer () { 17 | feh - 18 | } 19 | 20 | config_dir=${XDG_CONFIG_HOME:-$HOME/.config} 21 | cache_dir=${XDG_CACHE_HOME:-$HOME/.cache} 22 | 23 | # We expect to find these fields in pass(1)'s output 24 | URL_field='url' 25 | USERNAME_field='user' 26 | AUTOTYPE_field='autotype' 27 | OTPmethod_field='otp_method' 28 | 29 | default_autotype="user :tab pass" 30 | delay=2 31 | wait=0.2 32 | type_delay=12 33 | default_do='menu' # menu, copyPass, typeUser, typePass, copyUser, copyUrl, viewEntry, typeMenu, actionMenu, copyMenu, openUrl 34 | auto_enter='false' 35 | notify='false' 36 | clip=primary 37 | clip_clear=45 38 | default_user="${ROFI_PASS_DEFAULT_USER-$(whoami)}" 39 | default_user2=john_doe 40 | password_length=12 41 | fix_layout=false 42 | 43 | # default shortcuts 44 | autotype="Alt+1" 45 | type_user="Alt+2" 46 | type_pass="Alt+3" 47 | open_url="Alt+4" 48 | type_otp="Alt+5" 49 | copy_name="Alt+u" 50 | copy_url="Alt+l" 51 | copy_pass="Alt+p" 52 | show="Alt+o" 53 | copy_menu="Alt+c" 54 | action_menu="Alt+a" 55 | type_menu="Alt+t" 56 | help="Alt+h" 57 | switch="Alt+x" 58 | insert_pass="Alt+n" 59 | qrcode="Alt+q" 60 | previous_root="Shift+Left" 61 | next_root="Shift+Right" 62 | clipboard_backend=${ROFI_PASS_CLIPBOARD_BACKEND:-xclip} 63 | backend=${ROFI_PASS_BACKEND:-xdotool} 64 | 65 | case "$clipboard_backend" in 66 | "xclip");; 67 | "wl-clipboard");; 68 | *) 69 | >&2 echo "Invalid clipboard backend '$clipboard_backend', falling back to xclip" 70 | clipboard_backend=xclip 71 | ;; 72 | esac 73 | 74 | case "$backend" in 75 | "xdotool");; 76 | "wtype");; 77 | *) 78 | >&2 echo "Invalid backend '$backend', falling back to xdotool" 79 | backend=xdotool 80 | ;; 81 | esac 82 | 83 | # Safe permissions 84 | umask 077 85 | 86 | # Backends for clipboard manipulation 87 | _clip_in_primary_wl-clipboard() { 88 | wl-copy -p 89 | } 90 | 91 | _clip_in_clipboard_wl-clipboard() { 92 | wl-copy 93 | } 94 | 95 | _clip_out_primary_wl-clipboard() { 96 | wl-paste -p 97 | } 98 | 99 | _clip_out_clipboard_wl-clipboard() { 100 | wl-paste 101 | } 102 | 103 | _clip_in_primary_xclip() { 104 | xclip 105 | } 106 | 107 | _clip_in_clipboard_xclip() { 108 | xclip -selection clipboard 109 | } 110 | 111 | _clip_out_primary_xclip() { 112 | xclip -o 113 | } 114 | 115 | _clip_out_clipboard_xclip() { 116 | xclip --selection clipboard -o 117 | } 118 | 119 | # Backends for typing what's in stdin 120 | _do_type_xdotool() { 121 | xdotool type --delay ${type_delay} --clearmodifiers --file - 122 | } 123 | 124 | _do_type_wtype() { 125 | wtype -d ${type_delay} - 126 | } 127 | 128 | # Backends for pressing the key specified by the first argument ($1) 129 | _do_press_key_xdotool() { 130 | xdotool key "$1" 131 | } 132 | 133 | _do_press_key_wtype() { 134 | wtype -P "$1" -p "$1" 135 | } 136 | 137 | has_qrencode() { 138 | command -v qrencode >/dev/null 2>&1 139 | } 140 | 141 | listgpg () { 142 | mapfile -d '' pw_list < <(find -L . -name '*.gpg' -print0) 143 | pw_list=("${pw_list[@]#./}") 144 | printf '%s\n' "${pw_list[@]}" | sort -n 145 | } 146 | 147 | # get all password files and output as newline-delimited text 148 | list_passwords() { 149 | cd "${root}" || exit 150 | mapfile -t pw_list < <(listgpg) 151 | printf '%s\n' "${pw_list[@]%.gpg}" | sort -n 152 | } 153 | 154 | doClip () { 155 | case "$clip" in 156 | "primary") ${clip_in_primary} ;; 157 | "clipboard") ${clip_in_clipboard} ;; 158 | "both") ${clip_in_primary}; ${clip_out_primary} | ${clip_in_clipboard};; 159 | esac 160 | } 161 | 162 | checkIfPass () { 163 | printf '%s\n' "${root}: $selected_password" >| "$cache_dir/rofi-pass/last_used" 164 | } 165 | 166 | 167 | autopass () { 168 | if [[ $backend == "xdotool" ]]; then 169 | x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') 170 | xset r off 171 | fi 172 | 173 | rm -f "$cache_dir/rofi-pass/last_used" 174 | printf '%s\n' "${root}: $selected_password" > "$cache_dir/rofi-pass/last_used" 175 | for word in ${stuff["$AUTOTYPE_field"]}; do 176 | case "$word" in 177 | ":tab") ${do_press_key} Tab;; 178 | ":space") ${do_press_key} space;; 179 | ":delay") sleep "${delay}";; 180 | ":enter") ${do_press_key} Return;; 181 | ":otp") printf '%s' "$(generateOTP)" | ${do_type};; 182 | "pass") printf '%s' "${password}" | ${do_type};; 183 | "path") printf '%s' "${selected_password}" | rev | cut -d'/' -f1 | rev | ${do_type};; 184 | *) printf '%s' "${stuff[${word}]}" | ${do_type};; 185 | esac 186 | done 187 | 188 | if [[ ${auto_enter} == "true" ]]; then 189 | ${do_press_key} Return 190 | fi 191 | 192 | if [[ $backend == "xdotool" ]]; then 193 | xset r "$x_repeat_enabled" 194 | unset x_repeat_enabled 195 | fi 196 | 197 | clearUp 198 | } 199 | 200 | generateQrCode() { 201 | has_qrencode 202 | 203 | if [[ $? -eq "1" ]]; then 204 | printf '%s\n' "qrencode not found" | _rofi -dmenu 205 | exit_code=$? 206 | if [[ $exit_code -eq "1" ]]; then 207 | exit 208 | else 209 | "${basecommand}" 210 | fi 211 | fi 212 | 213 | checkIfPass 214 | pass "$selected_password" | head -n 1 | qrencode -d 300 -v 8 -l H -o - | _image_viewer 215 | if [[ $? -eq "1" ]]; then 216 | printf '%s\n' "" | _rofi -dmenu -mesg "Image viewer not defined or cannot read from pipe" 217 | exit_value=$? 218 | if [[ $exit_value -eq "1" ]]; then 219 | exit 220 | else 221 | "${basecommand}" 222 | fi 223 | fi 224 | clearUp 225 | } 226 | 227 | openURL () { 228 | checkIfPass 229 | $BROWSER "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${URL_field}: " | gawk '{sub(/:/,"")}{print $2}1' | head -1)"; exit; 230 | clearUp 231 | } 232 | 233 | typeUser () { 234 | checkIfPass 235 | 236 | if [[ $backend == "xdotool" ]]; then 237 | x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') 238 | xset r off 239 | fi 240 | 241 | printf '%s' "${stuff[${USERNAME_field}]}" | ${do_type} 242 | 243 | if [[ $backend == "xdotool" ]]; then 244 | xset r "$x_repeat_enabled" 245 | unset x_repeat_enabled 246 | fi 247 | 248 | clearUp 249 | } 250 | 251 | typePass () { 252 | checkIfPass 253 | 254 | if [[ $backend == "xdotool" ]]; then 255 | x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') 256 | xset r off 257 | fi 258 | 259 | printf '%s' "${password}" | ${do_type} 260 | 261 | if [[ $notify == "true" ]]; then 262 | if [[ "${stuff[notify]}" == "false" ]]; then 263 | : 264 | else 265 | notify-send "rofi-pass" "finished typing password"; 266 | fi 267 | elif [[ $notify == "false" ]]; then 268 | if [[ "${stuff[notify]}" == "true" ]]; then 269 | notify-send "rofi-pass" "finished typing password"; 270 | else 271 | : 272 | fi 273 | fi 274 | 275 | if [[ $backend == "xdotool" ]]; then 276 | xset r "$x_repeat_enabled" 277 | unset x_repeat_enabled 278 | fi 279 | 280 | clearUp 281 | } 282 | 283 | typeField () { 284 | checkIfPass 285 | local to_type 286 | 287 | if [[ $backend == "xdotool" ]]; then 288 | x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') 289 | xset r off 290 | fi 291 | 292 | case $typefield in 293 | "OTP") to_type="$(generateOTP)" ;; 294 | *) to_type="${stuff[${typefield}]}" ;; 295 | esac 296 | 297 | printf '%s' "$to_type" | ${do_type} 298 | 299 | if [[ $backend == "xdotool" ]]; then 300 | xset r "$x_repeat_enabled" 301 | unset x_repeat_enabled 302 | fi 303 | 304 | unset to_type 305 | 306 | clearUp 307 | } 308 | 309 | generateOTP () { 310 | checkIfPass 311 | 312 | # First, we check if there is a non-conventional OTP command in the pass file 313 | if PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep -q "${OTPmethod_field}: "; then 314 | # We execute the commands after otp_method: AS-IS 315 | bash -c "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${OTPmethod_field}: " | cut -d' ' -f2-)" 316 | else 317 | # If there is no method defined, fallback to pass-otp 318 | PASSWORD_STORE_DIR="${root}" pass otp "$selected_password" 319 | fi 320 | 321 | clearUp 322 | } 323 | 324 | copyUser () { 325 | checkIfPass 326 | printf '%s' "${stuff[${USERNAME_field}]}" | doClip 327 | clearUp 328 | } 329 | 330 | copyField () { 331 | checkIfPass 332 | printf '%s' "${stuff[${copyfield}]}" | doClip 333 | clearUp 334 | } 335 | 336 | copyURL () { 337 | checkIfPass 338 | printf '%s' "${stuff[${URL_field}]}" | doClip 339 | clearUp 340 | } 341 | 342 | copyPass () { 343 | checkIfPass 344 | printf '%s' "$password" | doClip 345 | if [[ $notify == "true" ]]; then 346 | notify-send "rofi-pass" "Copied Password\\nClearing in $clip_clear seconds" 347 | fi 348 | 349 | if [[ $notify == "true" ]]; then 350 | (sleep $clip_clear; printf '%s' "" | ${clip_in_primary}; printf '%s' "" | ${clip_in_clipboard} | notify-send "rofi-pass" "Clipboard cleared") & 351 | elif [[ $notify == "false" ]]; then 352 | (sleep $clip_clear; printf '%s' "" | ${clip_in_primary}; printf '%s' "" | ${clip_in_clipboard}) & 353 | fi 354 | } 355 | 356 | viewEntry () { 357 | checkIfPass 358 | showEntry "${selected_password}" 359 | } 360 | 361 | generatePass () { 362 | askmenu_content=( 363 | "Yes" 364 | "No" 365 | ) 366 | 367 | askGenMenu=$(printf '%s\n' "${askmenu_content[@]}" | _rofi -dmenu -p "Generate new Password for ${selected_password}? > ") 368 | askgen_exit=$? 369 | 370 | if [[ $askgen_exit -eq 1 ]]; then 371 | exit 372 | fi 373 | if [[ $askGenMenu == "Yes" ]]; then 374 | true 375 | elif [[ $askGenMenu == "No" ]]; then 376 | actionMenu 377 | fi 378 | 379 | checkIfPass 380 | 381 | symbols_content=( 382 | "0 Cancel" 383 | "1 Yes" 384 | "2 No" 385 | ) 386 | 387 | symbols=$(printf '%s\n' "${symbols_content[@]}" | _rofi -dmenu -p "Use Symbols? > ") 388 | symbols_val=$? 389 | 390 | if [[ $symbols_val -eq 1 ]]; then 391 | exit 392 | fi 393 | if [[ $symbols == "0 Cancel" ]]; then 394 | mainMenu; 395 | elif [[ $symbols == "1 Yes" ]]; then 396 | symbols=""; 397 | elif [[ $symbols == "2 No" ]]; then 398 | symbols="-n"; 399 | fi 400 | 401 | HELP="Enter Number or hit Enter to use default length" 402 | length=$(printf '%s' "" | _rofi -dmenu -mesg "${HELP}" -p "Password length? (Default: ${password_length}) > ") 403 | length_exit=$? 404 | 405 | if [[ $length_exit -eq 1 ]]; then 406 | exit 407 | fi 408 | if [[ $length == "" ]]; then 409 | PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${password_length}" > /dev/null; 410 | else 411 | PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${length}" > /dev/null; 412 | fi 413 | } 414 | 415 | # main Menu 416 | mainMenu () { 417 | if [[ $1 == "--bmarks" ]]; then 418 | selected_password="$(list_passwords 2>/dev/null \ 419 | | _rofi -mesg "Bookmarks Mode. ${switch} to switch" \ 420 | -dmenu \ 421 | -kb-custom-10 "${switch}" \ 422 | -select "$entry" \ 423 | -p "rofi-pass > ")" 424 | 425 | rofi_exit=$? 426 | 427 | if [[ $rofi_exit -eq 1 ]]; then 428 | exit 429 | elif [[ $rofi_exit -eq 19 ]]; then 430 | ${basecommand} 431 | elif [[ $rofi_exit -eq 0 ]]; then 432 | openURL 433 | fi 434 | else 435 | unset selected_password 436 | 437 | args=( -dmenu 438 | -kb-custom-1 "${autotype}" 439 | -kb-custom-2 "${type_user}" 440 | -kb-custom-3 "${type_pass}" 441 | -kb-custom-4 "${open_url}" 442 | -kb-custom-5 "${copy_name}" 443 | -kb-custom-6 "${copy_pass}" 444 | -kb-custom-7 "${show}" 445 | -kb-custom-8 "${copy_url}" 446 | -kb-custom-9 "${type_menu}" 447 | -kb-custom-10 "${previous_root}" 448 | -kb-custom-11 "${next_root}" 449 | -kb-custom-12 "${type_otp}" 450 | -kb-custom-14 "${action_menu}" 451 | -kb-custom-15 "${copy_menu}" 452 | -kb-custom-16 "${help}" 453 | -kb-custom-17 "${switch}" 454 | -kb-custom-18 "${insert_pass}" 455 | -kb-custom-19 "${qrcode}" 456 | ) 457 | args+=( -kb-mode-previous "" # These keyboard shortcut options are needed, because 458 | -kb-mode-next "" # Shift+ are otherwise taken by rofi. 459 | -select "$entry" 460 | -p "> " ) 461 | 462 | if [[ ${#roots[@]} -gt "1" || $custom_root == "true" ]]; then 463 | args+=(-mesg "PW Store: ${root}") 464 | fi 465 | 466 | selected_password="$(list_passwords 2>/dev/null | _rofi "${args[@]}")" 467 | 468 | rofi_exit=$? 469 | if [[ $rofi_exit -eq 1 ]]; then 470 | exit 471 | fi 472 | 473 | # Actions based on exit code, which do not need the entry. 474 | # The exit code for -kb-custom-X is X+9. 475 | case "${rofi_exit}" in 476 | 19) roots_index=$(( (roots_index-1+roots_length) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;; 477 | 20) roots_index=$(( (roots_index+1) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;; 478 | 25) helpMenu; return;; 479 | 26) ${basecommand} --bmarks; return;; 480 | esac 481 | 482 | mapfile -t password_temp < <(PASSWORD_STORE_DIR="${root}" pass show "$selected_password") 483 | password=${password_temp[0]} 484 | 485 | if [[ ${password} == "#FILE="* ]]; then 486 | pass_file="${password#*=}" 487 | mapfile -t password_temp2 < <(PASSWORD_STORE_DIR="${root}" pass show "${pass_file}") 488 | password=${password_temp2[0]} 489 | fi 490 | 491 | fields=$(printf '%s\n' "${password_temp[@]:1}" | awk '$1 ~ /:$/ || /otpauth:\/\// {$1=$1;print}') 492 | declare -A stuff 493 | stuff["pass"]=${password} 494 | 495 | if [[ -n $fields ]]; then 496 | while read -r LINE; do 497 | unset _id _val 498 | case "$LINE" in 499 | "otpauth://"*|"${OTPmethod_field}"*) 500 | _id="OTP" 501 | _val="" 502 | ;; 503 | *) 504 | _id="${LINE%%: *}" 505 | _val="${LINE#* }" 506 | ;; 507 | esac 508 | 509 | if [[ -n "$_id" ]]; then 510 | stuff["${_id}"]=${_val} 511 | fi 512 | done < <(printf '%s\n' "${fields}") 513 | 514 | if test "${stuff['autotype']+autotype}"; then 515 | : 516 | else 517 | stuff["autotype"]="${USERNAME_field} :tab pass" 518 | fi 519 | fi 520 | fi 521 | 522 | if [[ -z "${stuff["${AUTOTYPE_field}"]}" ]]; then 523 | if [[ -n $default_autotype ]]; then 524 | stuff["${AUTOTYPE_field}"]="${default_autotype}" 525 | fi 526 | fi 527 | if [[ -z "${stuff["${USERNAME_field}"]}" ]]; then 528 | if [[ -n $default_user ]]; then 529 | if [[ "$default_user" == ":filename" ]]; then 530 | stuff["${USERNAME_field}"]="$(basename "$selected_password")" 531 | else 532 | stuff["${USERNAME_field}"]="${default_user}" 533 | fi 534 | fi 535 | fi 536 | pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)" 537 | 538 | # actions based on keypresses 539 | # The exit code for -kb-custom-X is X+9. 540 | case "${rofi_exit}" in 541 | 0) typeMenu;; 542 | 10) sleep $wait; autopass;; 543 | 11) sleep $wait; typeUser;; 544 | 12) sleep $wait; typePass;; 545 | 13) openURL;; 546 | 14) copyUser;; 547 | 15) copyPass;; 548 | 16) viewEntry;; 549 | 17) copyURL;; 550 | 18) default_do="menu" typeMenu;; 551 | 21) sleep $wait; typefield=OTP; typeField;; 552 | 23) actionMenu;; 553 | 24) copyMenu;; 554 | 27) insertPass;; 555 | 28) generateQrCode;; 556 | esac 557 | clearUp 558 | } 559 | 560 | 561 | clearUp () { 562 | password='' 563 | selected_password='' 564 | unset stuff 565 | unset password 566 | unset selected_password 567 | unset password_temp 568 | unset stuff 569 | } 570 | 571 | helpMenu () { 572 | _rofi -dmenu -mesg "Hint: All hotkeys are configurable in config file" -p "Help > " <<- EOM 573 | ${autotype}: Autotype 574 | ${type_user}: Type Username 575 | ${type_pass}: Type Password 576 | ${type_otp}: Type OTP 577 | ${qrcode}: Generate and display qrcode 578 | --- 579 | ${copy_name}: Copy Username 580 | ${copy_pass}: Copy Password 581 | ${copy_url}: Copy URL 582 | ${open_url}: Open URL 583 | ${copy_menu}: Copy Custom Field 584 | --- 585 | ${action_menu}: Edit, Move, Delete, Re-generate Submenu 586 | ${show}: Show Password File 587 | ${insert_pass}: Insert new Pass Entry 588 | ${switch}: Switch Pass/Bookmark Mode 589 | --- 590 | ${previous_root}: Switch to previous password store (--root) 591 | ${next_root}: Switch to next password store (--root) 592 | EOM 593 | help_val=$? 594 | 595 | if [[ $help_val -eq 1 ]]; then 596 | exit; 597 | else 598 | unset helptext; mainMenu; 599 | fi 600 | } 601 | 602 | 603 | typeMenu () { 604 | if [[ -n $default_do ]]; then 605 | if [[ $default_do == "menu" ]]; then 606 | checkIfPass 607 | local -a keys=("${!stuff[@]}") 608 | keys=("${keys[@]/$AUTOTYPE_field}") 609 | typefield=$({ printf '%s' "${AUTOTYPE_field}" ; printf '%s\n' "${keys[@]}" | sort; } | _rofi -dmenu -p "Choose Field to type > ") 610 | typefield_exit=$? 611 | if [[ $typefield_exit -eq 1 ]]; then 612 | exit 613 | fi 614 | case "$typefield" in 615 | '') exit;; 616 | 'pass') sleep $wait; typePass;; 617 | "${AUTOTYPE_field}") sleep $wait; autopass;; 618 | *) sleep $wait; typeField 619 | esac 620 | clearUp 621 | elif [[ $default_do == "${AUTOTYPE_field}" ]]; then 622 | sleep $wait; autopass 623 | else 624 | ${default_do} 625 | fi 626 | fi 627 | } 628 | 629 | copyMenu () { 630 | checkIfPass 631 | copyfield=$(printf '%s\n' "${!stuff[@]}" | sort | _rofi -dmenu -p "Choose Field to copy > ") 632 | val=$? 633 | if [[ $val -eq 1 ]]; then 634 | exit; 635 | fi 636 | if [[ $copyfield == "pass" ]]; then 637 | copyPass; 638 | else 639 | copyField 640 | fi 641 | clearUp 642 | } 643 | 644 | actionMenu () { 645 | checkIfPass 646 | action_content=("< Return" 647 | "---" 648 | "1 Move Password File" 649 | "2 Copy Password File" 650 | "3 Delete Password File" 651 | "4 Edit Password File" 652 | "5 Generate New Password" 653 | ) 654 | 655 | action=$(printf '%s\n' "${action_content[@]}" | _rofi -dmenu -p "Choose Action > ") 656 | if [[ ${action} == "1 Move Password File" ]]; then 657 | manageEntry move; 658 | elif [[ ${action} == "3 Delete Password File" ]]; then 659 | manageEntry delete; 660 | elif [[ ${action} == "2 Copy Password File" ]]; then 661 | manageEntry copy; 662 | elif [[ ${action} == "4 Edit Password File" ]]; then 663 | manageEntry edit; 664 | elif [[ ${action} == "5 Generate New Password" ]]; then 665 | generatePass; 666 | elif [[ ${action} == "< Return" ]]; then 667 | mainMenu; 668 | elif [[ ${action} == "" ]]; then 669 | exit 670 | fi 671 | } 672 | 673 | showEntry () { 674 | if [[ -z $pass_content ]]; then 675 | pass_temp=$(PASSWORD_STORE_DIR="${root}" pass show "$selected_password") 676 | password="${pass_temp%%$'\n'*}" 677 | pass_key_value=$(printf '%s\n' "${pass_temp}" | tail -n+2 | grep ': ') 678 | declare -A stuff 679 | 680 | while read -r LINE; do 681 | _id="${LINE%%: *}" 682 | _val="${LINE#* }" 683 | stuff["${_id}"]=${_val} 684 | done < <(printf '%s\n' "${pass_key_value}") 685 | 686 | stuff["pass"]=${password} 687 | 688 | if test "${stuff['autotype']+autotype}"; then 689 | : 690 | else 691 | stuff["autotype"]="${USERNAME_field} :tab pass" 692 | fi 693 | 694 | pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)" 695 | fi 696 | 697 | bla_content=("< Return" 698 | "${pass_content}" 699 | ) 700 | 701 | bla=$(printf '%s\n' "${bla_content[@]}" | _rofi -dmenu -mesg "Enter: Copy entry to clipboard" -p "> ") 702 | rofi_exit=$? 703 | 704 | word=$(printf '%s' "$bla" | gawk -F': ' '{print $1}') 705 | 706 | if [[ ${rofi_exit} -eq 1 ]]; then 707 | exit 708 | elif [[ ${rofi_exit} -eq 0 ]]; then 709 | if [[ ${bla} == "< Return" ]]; then 710 | mainMenu 711 | else 712 | if [[ -z $(printf '%s' "${stuff[${word}]}") ]]; then 713 | printf '%s' "$word" | doClip 714 | else 715 | printf '%s' "${stuff[${word}]}" | doClip 716 | fi 717 | if [[ $notify == "true" ]]; then 718 | notify-send "rofi-pass" "Copied Password\\nClearing in $clip_clear seconds" 719 | fi 720 | if [[ $notify == "true" ]]; then 721 | (sleep $clip_clear; printf '%s' "" | ${clip_in_primary}; printf '%s' "" | ${clip_in_clipboard} | notify-send "rofi-pass" "Clipboard cleared") & 722 | elif [[ $notify == "false" ]]; then 723 | (sleep $clip_clear; printf '%s' "" | ${clip_in_primary}; printf '%s' "" | ${clip_in_clipboard}) & 724 | fi 725 | exit 726 | fi 727 | fi 728 | exit 729 | unset stuff 730 | unset password 731 | unset selected_password 732 | unset password_temp 733 | unset stuff 734 | exit 735 | } 736 | 737 | manageEntry () { 738 | if [[ "$1" == "edit" ]]; then 739 | EDITOR=$EDITOR PASSWORD_STORE_DIR="${root}" pass edit "${selected_password}" 740 | mainMenu 741 | elif [[ $1 == "move" ]]; then 742 | cd "${root}" || exit 743 | group_array=(*/) 744 | group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ") 745 | if [[ $group == "" ]]; then 746 | exit 747 | fi 748 | PASSWORD_STORE_DIR="${root}" pass mv "$selected_password" "${group}" 749 | mainMenu 750 | elif [[ $1 == "copy" ]]; then 751 | cd "${root}" || exit 752 | group_array=(*/) 753 | group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ") 754 | if [[ $group == "" ]]; then 755 | exit 756 | else 757 | new_name="$(listgpg | _rofi -dmenu -format 'f' -mesg "Copying to same Group. Please enter a name for the new entry" -p "> ")" 758 | fi 759 | PASSWORD_STORE_DIR="${root}" pass cp "$selected_password" "${group}/${new_name}" 760 | mainMenu 761 | elif [[ "$1" == "delete" ]]; then 762 | HELP="Selected entry: ${selected_password}" 763 | ask_content=("Yes" 764 | "No" 765 | ) 766 | ask=$(printf '%s\n' "${ask_content[@]}" | _rofi -mesg "${HELP}" -dmenu -p "Are You Sure? > ") 767 | if [[ "$ask" == "Yes" ]]; then 768 | PASSWORD_STORE_DIR="${root}" pass rm --force "${selected_password}" 769 | elif [[ "$ask" == "No" ]]; then 770 | mainMenu 771 | elif [[ -z "$ask" ]]; then 772 | exit 773 | fi 774 | else 775 | mainMenu 776 | fi 777 | } 778 | 779 | edit_pass() { 780 | if [[ $edit_new_pass == "true" ]]; then 781 | PASSWORD_STORE_DIR="${root}" pass edit "${1}" 782 | fi 783 | } 784 | 785 | insertPass () { 786 | url=$(${clip_out_clipboard}) 787 | 788 | if [[ "${url:0:4}" == "http" ]]; then 789 | domain_name="$(printf '%s\n' "${url}" | awk -F / '{l=split($3,a,"."); print (a[l-1]=="com"?a[l-2] OFS:X) a[l-1] OFS a[l]}' OFS=".")" 790 | help_content="Domain: ${domain_name} 791 | Type name, make sure it is unique" 792 | else 793 | help_content="Hint: Copy URL to clipboard before calling this menu. 794 | Type name, make sure it is unique" 795 | fi 796 | 797 | cd "${root}" || exit 798 | group_array=(*/) 799 | grouplist=$(printf '%s\n' "${group_array[@]%/}") 800 | name="$(listgpg | _rofi -dmenu -format 'f' -filter "${domain_name}" -mesg "${help_content}" -p "> ")" 801 | val=$? 802 | 803 | if [[ $val -eq 1 ]]; then 804 | exit 805 | fi 806 | 807 | user_content=("${default_user2}" 808 | "${USER}" 809 | "${default_user}" 810 | ) 811 | 812 | user=$(printf '%s\n' "${user_content[@]}" | _rofi -dmenu -mesg "Chose Username or type" -p "> ") 813 | val=$? 814 | 815 | if [[ $val -eq 1 ]]; then 816 | exit 817 | fi 818 | 819 | group_content=("No Group" 820 | "---" 821 | "${grouplist}" 822 | ) 823 | 824 | group=$(printf '%s\n' "${group_content[@]}" | _rofi -dmenu -p "Choose Group > ") 825 | val=$? 826 | 827 | if [[ $val -eq 1 ]]; then 828 | exit 829 | fi 830 | 831 | pw=$(printf '%s' "Generate" | _rofi -dmenu -password -p "Password > " -mesg "Type Password or hit Enter to generate one") 832 | 833 | if [[ $pw == "Generate" ]]; then 834 | pw=$(_pwgen "${password_length}") 835 | fi 836 | 837 | clear 838 | 839 | if [[ "$group" == "No Group" ]]; then 840 | if [[ $url == http* ]]; then 841 | pass_content=("${pw}" 842 | "---" 843 | "${USERNAME_field}: ${user}" 844 | "${URL_field}: ${url}" 845 | ) 846 | printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}" 847 | else 848 | pass_content=("${pw}" 849 | "---" 850 | "${USERNAME_field}: ${user}" 851 | ) 852 | printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}" 853 | fi 854 | else 855 | if [[ $url == http* ]]; then 856 | pass_content=("${pw}" 857 | "---" 858 | "${USERNAME_field}: ${user}" 859 | "${URL_field}: ${url}" 860 | ) 861 | printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}" 862 | else 863 | pass_content=("${pw}" 864 | "---" 865 | "${USERNAME_field}: ${user}" 866 | ) 867 | printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}" 868 | fi 869 | fi 870 | } 871 | 872 | help_msg () { 873 | cat <<'EOF' 874 | Usage: 875 | rofi-pass [command] 876 | 877 | Commands: 878 | --insert insert new entry to password store 879 | --root set custom root directories (colon separated) 880 | --last-used highlight last used item 881 | --show-last show details of last used Entry 882 | --bmarks start in bookmarks mode 883 | 884 | rofi-pass version 1.5.3 885 | EOF 886 | } 887 | 888 | get_config_file () { 889 | configs=("$ROFI_PASS_CONFIG" 890 | "$config_dir/rofi-pass/config" 891 | "/etc/rofi-pass.conf") 892 | 893 | # return the first config file with a valid path 894 | for config in "${configs[@]}"; do 895 | # '-n' is needed in case ROFI_PASS_CONFIG is not set 896 | if [[ -n "${config}" && -f "${config}" ]]; then 897 | printf "%s" "$config" 898 | return 899 | fi 900 | done 901 | } 902 | 903 | main () { 904 | # load config file 905 | config_file="$(get_config_file)" 906 | [[ -n "$config_file" ]] && source "$config_file" 907 | 908 | # create tmp dir 909 | if [[ ! -d "$cache_dir/rofi-pass" ]]; then 910 | mkdir -p "$cache_dir/rofi-pass" 911 | fi 912 | 913 | # set backends 914 | clip_in_primary=_clip_in_primary_${clipboard_backend} 915 | clip_in_clipboard=_clip_in_clipboard_${clipboard_backend} 916 | clip_out_primary=_clip_out_primary_${clipboard_backend} 917 | clip_out_clipboard=_clip_out_clipboard_${clipboard_backend} 918 | 919 | do_type=_do_type_${backend} 920 | do_press_key=_do_press_key_${backend} 921 | 922 | # backwards compat 923 | if [[ -n "$xdotool_delay" ]]; then 924 | >&2 echo "Setting 'xdotool_delay' is deprecated. Please update your configuration to use 'type_delay' instead" 925 | type_delay=${xdotool_delay} 926 | fi 927 | 928 | # Only valid for the xdotool backend 929 | if [[ $backend == "xdotool" ]]; then 930 | # fix keyboard layout if enabled in config 931 | if [[ $fix_layout == "true" ]]; then 932 | layout_cmd 933 | fi 934 | fi 935 | 936 | # check for BROWSER variable, use xdg-open as fallback 937 | if [[ -z $BROWSER ]]; then 938 | export BROWSER=xdg-open 939 | fi 940 | 941 | # check if alternative root directory was given on commandline 942 | if [[ -r "$cache_dir/rofi-pass/last_used" ]] && [[ $1 == "--last-used" || $1 == "--show-last" ]]; then 943 | roots=("$(awk -F ': ' '{ print $1 }' "$cache_dir/rofi-pass/last_used")") 944 | elif [[ -n "$2" && "$1" == "--root" ]]; then 945 | custom_root=true; IFS=: read -r -a roots <<< "$2" 946 | elif [[ -n $root ]]; then 947 | custom_root=true; IFS=: read -r -a roots <<< "${root}" 948 | elif [[ -n ${PASSWORD_STORE_DIR} ]]; then 949 | roots=("${PASSWORD_STORE_DIR}") 950 | else 951 | roots=("$HOME/.password-store") 952 | fi 953 | roots_index=0 954 | roots_length=${#roots[@]} 955 | export root=${roots[$roots_index]} 956 | export PASSWORD_STORE_DIR="${root}" 957 | case $1 in 958 | --insert) 959 | insertPass 960 | ;; 961 | --root) 962 | mainMenu 963 | ;; 964 | --help) 965 | help_msg 966 | ;; 967 | --last-used) 968 | if [[ -r "$cache_dir/rofi-pass/last_used" ]]; then 969 | entry="$(awk -F ': ' '{ print $2 }' "$cache_dir/rofi-pass/last_used")" 970 | fi 971 | mainMenu 972 | ;; 973 | --show-last) 974 | if [[ -r "$cache_dir/rofi-pass/last_used" ]]; then 975 | selected_password="$(awk -F ': ' '{ print $2 }' "$cache_dir/rofi-pass/last_used")" viewEntry 976 | else 977 | mainMenu 978 | fi 979 | ;; 980 | --bmarks) 981 | mainMenu --bmarks; 982 | ;; 983 | *) 984 | mainMenu 985 | ;; 986 | esac 987 | } 988 | 989 | main "$@" 990 | -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carnager/rofi-pass/37c4c862deb133a85b7d72989acfdbd2ef16b8ad/screenshot.jpg --------------------------------------------------------------------------------