Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim-plugins.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Hörist <philipp@hoerist.com>2023-04-15 17:15:01 +0300
committerPhilipp Hörist <philipp@hoerist.com>2023-04-15 17:15:01 +0300
commit719de5a1f4f00bbdd293828973f4bcc5031d1683 (patch)
tree247fabe4625dbc45a87693bccf65efa7bbddd456
parentad31a04d090c2ff13ba031b68ce555aea3b6f26f (diff)
[omemo] Remove plugin
It has been integrated into Gajim
-rw-r--r--omemo/COPYING674
-rw-r--r--omemo/__init__.py1
-rw-r--r--omemo/backend/__init__.py1
-rw-r--r--omemo/backend/aes.py95
-rw-r--r--omemo/backend/devices.py138
-rw-r--r--omemo/backend/liteaxolotlstore.py741
-rw-r--r--omemo/backend/state.py362
-rw-r--r--omemo/backend/util.py56
-rw-r--r--omemo/gtk/__init__.py0
-rw-r--r--omemo/gtk/config.py183
-rw-r--r--omemo/gtk/config.ui614
-rw-r--r--omemo/gtk/key.py454
-rw-r--r--omemo/gtk/key.ui332
-rw-r--r--omemo/gtk/progress.py53
-rw-r--r--omemo/gtk/progress.ui123
-rw-r--r--omemo/gtk/style.css18
-rw-r--r--omemo/modules/__init__.py0
-rw-r--r--omemo/modules/events.py28
-rw-r--r--omemo/modules/omemo.py514
-rw-r--r--omemo/modules/util.py30
-rw-r--r--omemo/omemo.pngbin2500 -> 0 bytes
-rw-r--r--omemo/omemo16x16.pngbin688 -> 0 bytes
-rw-r--r--omemo/org.gajim.Gajim.Plugin.omemo.metainfo.xml12
-rw-r--r--omemo/plugin-manifest.json22
-rw-r--r--omemo/plugin.py342
25 files changed, 0 insertions, 4793 deletions
diff --git a/omemo/COPYING b/omemo/COPYING
deleted file mode 100644
index 818433e..0000000
--- a/omemo/COPYING
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- 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.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- 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 <http://www.gnu.org/licenses/>.
-
-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:
-
- <program> Copyright (C) <year> <name of author>
- 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
-<http://www.gnu.org/licenses/>.
-
- 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
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/omemo/__init__.py b/omemo/__init__.py
deleted file mode 100644
index 53d9664..0000000
--- a/omemo/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from omemo.plugin import OmemoPlugin
diff --git a/omemo/backend/__init__.py b/omemo/backend/__init__.py
deleted file mode 100644
index 3dc1f76..0000000
--- a/omemo/backend/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__version__ = "0.1.0"
diff --git a/omemo/backend/aes.py b/omemo/backend/aes.py
deleted file mode 100644
index 49d288a..0000000
--- a/omemo/backend/aes.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-
-import os
-import logging
-from collections import namedtuple
-
-from cryptography.hazmat.primitives.ciphers import Cipher
-from cryptography.hazmat.primitives.ciphers import algorithms
-from cryptography.hazmat.primitives.ciphers.modes import GCM
-from cryptography.hazmat.backends import default_backend
-
-log = logging.getLogger('gajim.p.omemo')
-
-EncryptionResult = namedtuple('EncryptionResult', 'payload key iv')
-
-IV_SIZE = 12
-
-def _decrypt(key, iv, tag, data):
- decryptor = Cipher(
- algorithms.AES(key),
- GCM(iv, tag=tag),
- backend=default_backend()).decryptor()
- return decryptor.update(data) + decryptor.finalize()
-
-
-def aes_decrypt(_key, iv, payload):
- if len(_key) >= 32:
- # XEP-0384
- log.debug('XEP Compliant Key/Tag')
- data = payload
- key = _key[:16]
- tag = _key[16:]
- else:
- # Legacy
- log.debug('Legacy Key/Tag')
- data = payload[:-16]
- key = _key
- tag = payload[-16:]
-
- return _decrypt(key, iv, tag, data).decode()
-
-
-def aes_decrypt_file(key, iv, payload):
- data = payload[:-16]
- tag = payload[-16:]
- return _decrypt(key, iv, tag, data)
-
-
-def _encrypt(data, key_size, iv_size=IV_SIZE):
- if isinstance(data, str):
- data = data.encode()
- key = os.urandom(key_size)
- iv = os.urandom(iv_size)
- encryptor = Cipher(
- algorithms.AES(key),
- GCM(iv),
- backend=default_backend()).encryptor()
-
- payload = encryptor.update(data) + encryptor.finalize()
- return key, iv, encryptor.tag, payload
-
-
-def aes_encrypt(plaintext):
- key, iv, tag, payload = _encrypt(plaintext, 16)
- key += tag
- return EncryptionResult(payload=payload, key=key, iv=iv)
-
-
-def aes_encrypt_file(data):
- key, iv, tag, payload, = _encrypt(data, 32)
- payload += tag
- return EncryptionResult(payload=payload, key=key, iv=iv)
-
-
-def get_new_key():
- return os.urandom(16)
-
-
-def get_new_iv():
- return os.urandom(IV_SIZE)
diff --git a/omemo/backend/devices.py b/omemo/backend/devices.py
deleted file mode 100644
index 3790785..0000000
--- a/omemo/backend/devices.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-from collections import defaultdict
-
-from gajim.common import app
-
-from omemo.backend.util import UNACKNOWLEDGED_COUNT
-
-
-class DeviceManager:
- def __init__(self):
- self.__device_store = defaultdict(set)
- self.__muc_member_store = defaultdict(set)
-
- reg_id = self._storage.getLocalRegistrationId()
- if reg_id is None:
- raise ValueError('No own device found')
-
- self.__own_device = reg_id
- self.add_device(self._own_jid, self.__own_device)
- self._log.info('Our device id: %s', self.__own_device)
-
- for jid, device in self._storage.getActiveDeviceTuples():
- self._log.info('Load device from storage: %s - %s', jid, device)
- self.add_device(jid, device)
-
- def update_devicelist(self, jid, devicelist):
- for device in list(devicelist):
- if device == self.own_device:
- continue
- count = self._storage.getUnacknowledgedCount(jid, device)
- if count > UNACKNOWLEDGED_COUNT:
- self._log.warning('Ignore device because of %s unacknowledged'
- ' messages: %s %s', count, jid, device)
- devicelist.remove(device)
-
- self.__device_store[jid] = set(devicelist)
- self._log.info('Saved devices for %s', jid)
- self._storage.setActiveState(jid, devicelist)
-
- def add_muc_member(self, room_jid, jid):
- self._log.info('Saved MUC member %s %s', room_jid, jid)
- self.__muc_member_store[room_jid].add(jid)
-
- def remove_muc_member(self, room_jid, jid):
- self._log.info('Removed MUC member %s %s', room_jid, jid)
- self.__muc_member_store[room_jid].discard(jid)
-
- def get_muc_members(self, room_jid, without_self=True):
- members = set(self.__muc_member_store[room_jid])
- if without_self:
- members.discard(self._own_jid)
- return members
-
- def add_device(self, jid, device):
- self.__device_store[jid].add(device)
-
- def remove_device(self, jid, device):
- self.__device_store[jid].discard(device)
- self._storage.setInactive(jid, device)
-
- def get_devices(self, jid, without_self=False):
- devices = set(self.__device_store[jid])
- if without_self:
- devices.discard(self.own_device)
- return devices
-
- def get_devices_for_encryption(self, jid):
- devices_for_encryption = []
-
- client = app.get_client(self._account)
- contact = client.get_module('Contacts').get_contact(jid)
- if contact.is_groupchat:
- devices_for_encryption = self._get_devices_for_muc_encryption(jid)
- else:
- devices_for_encryption = self._get_devices_for_encryption(jid)
-
- if not devices_for_encryption:
- raise NoDevicesFound
-
- devices_for_encryption += self._get_own_devices_for_encryption()
- return set(devices_for_encryption)
-
- def _get_devices_for_muc_encryption(self, jid):
- devices_for_encryption = []
- for jid_ in self.__muc_member_store[jid]:
- devices_for_encryption += self._get_devices_for_encryption(jid_)
- return devices_for_encryption
-
- def _get_own_devices_for_encryption(self):
- devices_for_encryption = []
- own_devices = self.get_devices(self._own_jid, without_self=True)
- for device in own_devices:
- if self._storage.isTrusted(self._own_jid, device):
- devices_for_encryption.append((self._own_jid, device))
- return devices_for_encryption
-
- def _get_devices_for_encryption(self, jid):
- devices_for_encryption = []
- devices = self.get_devices(jid)
-
- for device in devices:
- if self._storage.isTrusted(jid, device):
- devices_for_encryption.append((jid, device))
- return devices_for_encryption
-
- @property
- def own_device(self):
- return self.__own_device
-
- @property
- def devices_for_publish(self):
- devices = self.get_devices(self._own_jid)
- if self.own_device not in devices:
- devices.add(self.own_device)
- return devices
-
- @property
- def is_own_device_published(self):
- return self.own_device in self.get_devices(self._own_jid)
-
-
-class NoDevicesFound(Exception):
- pass
diff --git a/omemo/backend/liteaxolotlstore.py b/omemo/backend/liteaxolotlstore.py
deleted file mode 100644
index c1004bc..0000000
--- a/omemo/backend/liteaxolotlstore.py
+++ /dev/null
@@ -1,741 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-# Copyright (C) 2015 Tarek Galal <tare2.galal@gmail.com>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-import time
-import sqlite3
-from collections import namedtuple
-
-from axolotl.state.axolotlstore import AxolotlStore
-from axolotl.state.signedprekeyrecord import SignedPreKeyRecord
-from axolotl.state.sessionrecord import SessionRecord
-from axolotl.state.prekeyrecord import PreKeyRecord
-from axolotl.invalidkeyidexception import InvalidKeyIdException
-from axolotl.ecc.djbec import DjbECPrivateKey
-from axolotl.ecc.djbec import DjbECPublicKey
-from axolotl.identitykeypair import IdentityKeyPair
-from axolotl.util.medium import Medium
-from axolotl.util.keyhelper import KeyHelper
-
-from gajim.common import app
-
-from omemo.backend.util import Trust
-from omemo.backend.util import IdentityKeyExtended
-from omemo.backend.util import DEFAULT_PREKEY_AMOUNT
-
-
-def _convert_to_string(text):
- return text.decode()
-
-
-def _convert_identity_key(key):
- if not key:
- return
- return IdentityKeyExtended(DjbECPublicKey(key[1:]))
-
-
-def _convert_record(record):
- return SessionRecord(serialized=record)
-
-
-sqlite3.register_converter('pk', _convert_identity_key)
-sqlite3.register_converter('session_record', _convert_record)
-
-
-class LiteAxolotlStore(AxolotlStore):
- def __init__(self, db_path, log):
- self._log = log
- self._con = sqlite3.connect(db_path,
- detect_types=sqlite3.PARSE_COLNAMES)
- self._con.row_factory = self._namedtuple_factory
- self.createDb()
- self.migrateDb()
-
- self._con.execute("PRAGMA secure_delete=1")
- self._con.execute("PRAGMA synchronous=NORMAL;")
- mode = self._con.execute("PRAGMA journal_mode;").fetchone()[0]
-
- # WAL is a persistent DB mode, don't override it if user has set it
- if mode != 'wal':
- self._con.execute("PRAGMA journal_mode=MEMORY;")
- self._con.commit()
-
- if not self.getLocalRegistrationId():
- self._log.info("Generating OMEMO keys")
- self._generate_axolotl_keys()
-
- @staticmethod
- def _is_blind_trust_enabled():
- plugin = app.plugin_manager.get_active_plugin('omemo')
- return plugin.config['BLIND_TRUST']
-
- @staticmethod
- def _namedtuple_factory(cursor, row):
- fields = []
- for col in cursor.description:
- if col[0] == '_id':
- fields.append('id')
- elif 'strftime' in col[0]:
- fields.append('formated_time')
- elif 'MAX' in col[0] or 'COUNT' in col[0]:
- col_name = col[0].replace('(', '_')
- col_name = col_name.replace(')', '')
- fields.append(col_name.lower())
- else:
- fields.append(col[0])
- return namedtuple("Row", fields)(*row)
-
- def _generate_axolotl_keys(self):
- identity_key_pair = KeyHelper.generateIdentityKeyPair()
- registration_id = KeyHelper.getRandomSequence(2147483647)
- pre_keys = KeyHelper.generatePreKeys(
- KeyHelper.getRandomSequence(4294967296),
- DEFAULT_PREKEY_AMOUNT)
- self.storeLocalData(registration_id, identity_key_pair)
-
- signed_pre_key = KeyHelper.generateSignedPreKey(
- identity_key_pair, KeyHelper.getRandomSequence(65536))
-
- self.storeSignedPreKey(signed_pre_key.getId(), signed_pre_key)
-
- for pre_key in pre_keys:
- self.storePreKey(pre_key.getId(), pre_key)
-
- def user_version(self):
- return self._con.execute('PRAGMA user_version').fetchone()[0]
-
- def createDb(self):
- if self.user_version() == 0:
-
- create_tables = '''
- CREATE TABLE IF NOT EXISTS secret (
- device_id INTEGER, public_key BLOB, private_key BLOB);
-
- CREATE TABLE IF NOT EXISTS identities (
- _id INTEGER PRIMARY KEY AUTOINCREMENT, recipient_id TEXT,
- registration_id INTEGER, public_key BLOB,
- timestamp INTEGER, trust INTEGER,
- shown INTEGER DEFAULT 0);
-
- CREATE UNIQUE INDEX IF NOT EXISTS
- public_key_index ON identities (public_key, recipient_id);
-
- CREATE TABLE IF NOT EXISTS prekeys(
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- prekey_id INTEGER UNIQUE, sent_to_server BOOLEAN,
- record BLOB);
-
- CREATE TABLE IF NOT EXISTS signed_prekeys (
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- prekey_id INTEGER UNIQUE,
- timestamp NUMERIC DEFAULT CURRENT_TIMESTAMP, record BLOB);
-
- CREATE TABLE IF NOT EXISTS sessions (
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- recipient_id TEXT, device_id INTEGER,
- record BLOB, timestamp INTEGER, active INTEGER DEFAULT 1,
- UNIQUE(recipient_id, device_id));
-
- '''
-
- create_db_sql = """
- BEGIN TRANSACTION;
- %s
- PRAGMA user_version=12;
- END TRANSACTION;
- """ % (create_tables)
- self._con.executescript(create_db_sql)
-
- def migrateDb(self):
- """ Migrates the DB
- """
-
- # Find all double entries and delete them
- if self.user_version() < 2:
- delete_dupes = """ DELETE FROM identities WHERE _id not in (
- SELECT MIN(_id)
- FROM identities
- GROUP BY
- recipient_id, public_key
- );
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=2;
- END TRANSACTION;
- """ % (delete_dupes))
-
- if self.user_version() < 3:
- # Create a UNIQUE INDEX so every public key/recipient_id tuple
- # can only be once in the db
- add_index = """ CREATE UNIQUE INDEX IF NOT EXISTS
- public_key_index
- ON identities (public_key, recipient_id);
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=3;
- END TRANSACTION;
- """ % (add_index))
-
- if self.user_version() < 4:
- # Adds column "active" to the sessions table
- add_active = """ ALTER TABLE sessions
- ADD COLUMN active INTEGER DEFAULT 1;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=4;
- END TRANSACTION;
- """ % (add_active))
-
- if self.user_version() < 5:
- # Adds DEFAULT Timestamp
- add_timestamp = """
- DROP TABLE signed_prekeys;
- CREATE TABLE IF NOT EXISTS signed_prekeys (
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- prekey_id INTEGER UNIQUE,
- timestamp NUMERIC DEFAULT CURRENT_TIMESTAMP, record BLOB);
- ALTER TABLE identities ADD COLUMN shown INTEGER DEFAULT 0;
- UPDATE identities SET shown = 1;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=5;
- END TRANSACTION;
- """ % (add_timestamp))
-
- if self.user_version() < 6:
- # Move secret data into own table
- # We add +1 to registration id because we did that in other code in
- # earlier versions. On this migration we correct this mistake now.
- move = """
- CREATE TABLE IF NOT EXISTS secret (
- device_id INTEGER, public_key BLOB, private_key BLOB);
- INSERT INTO secret (device_id, public_key, private_key)
- SELECT registration_id + 1, public_key, private_key
- FROM identities
- WHERE recipient_id = -1;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=6;
- END TRANSACTION;
- """ % move)
-
- if self.user_version() < 7:
- # Convert old device ids to integer
- convert = """
- UPDATE secret SET device_id = device_id % 2147483646;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=7;
- END TRANSACTION;
- """ % convert)
-
- if self.user_version() < 8:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT recipient_id,
- registration_id,
- CAST(public_key as BLOB) as public_key,
- CAST(private_key as BLOB) as private_key,
- timestamp, trust, shown
- FROM identities'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM identities'
- self._con.execute(delete)
-
- insert = '''INSERT INTO identities (
- recipient_id, registration_id, public_key, private_key,
- timestamp, trust, shown)
- VALUES (?, ?, ?, ?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=8')
- self._con.commit()
-
- if self.user_version() < 9:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT device_id,
- CAST(public_key as BLOB) as public_key,
- CAST(private_key as BLOB) as private_key
- FROM secret'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM secret'
- self._con.execute(delete)
-
- insert = '''INSERT INTO secret (device_id, public_key, private_key)
- VALUES (?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=9')
- self._con.commit()
-
- if self.user_version() < 10:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT _id,
- recipient_id,
- device_id,
- CAST(record as BLOB) as record,
- timestamp,
- active
- FROM sessions'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM sessions'
- self._con.execute(delete)
-
- insert = '''INSERT INTO sessions (_id, recipient_id, device_id,
- record, timestamp, active)
- VALUES (?, ?, ?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=10')
- self._con.commit()
-
- if self.user_version() < 11:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT _id,
- prekey_id,
- sent_to_server,
- CAST(record as BLOB) as record
- FROM prekeys'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM prekeys'
- self._con.execute(delete)
-
- insert = '''INSERT INTO prekeys (
- _id, prekey_id, sent_to_server, record)
- VALUES (?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=11')
- self._con.commit()
-
- if self.user_version() < 12:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT _id,
- prekey_id,
- timestamp,
- CAST(record as BLOB) as record
- FROM signed_prekeys'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM signed_prekeys'
- self._con.execute(delete)
-
- insert = '''INSERT INTO signed_prekeys (
- _id, prekey_id, timestamp, record)
- VALUES (?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=12')
- self._con.commit()
-
- def loadSignedPreKey(self, signedPreKeyId):
- query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
- result = self._con.execute(query, (signedPreKeyId, )).fetchone()
- if result is None:
- raise InvalidKeyIdException("No such signedprekeyrecord! %s " %
- signedPreKeyId)
- return SignedPreKeyRecord(serialized=result.record)
-
- def loadSignedPreKeys(self):
- query = 'SELECT record FROM signed_prekeys'
- results = self._con.execute(query).fetchall()
- return [SignedPreKeyRecord(serialized=row.record) for row in results]
-
- def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord):
- query = 'INSERT INTO signed_prekeys (prekey_id, record) VALUES(?,?)'
- self._con.execute(query, (signedPreKeyId,
- signedPreKeyRecord.serialize()))
- self._con.commit()
-
- def containsSignedPreKey(self, signedPreKeyId):
- query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
- result = self._con.execute(query, (signedPreKeyId,)).fetchone()
- return result is not None
-
- def removeSignedPreKey(self, signedPreKeyId):
- query = 'DELETE FROM signed_prekeys WHERE prekey_id = ?'
- self._con.execute(query, (signedPreKeyId,))
- self._con.commit()
-
- def getNextSignedPreKeyId(self):
- result = self.getCurrentSignedPreKeyId()
- if result is None:
- return 1 # StartId if no SignedPreKeys exist
- return (result % (Medium.MAX_VALUE - 1)) + 1
-
- def getCurrentSignedPreKeyId(self):
- query = 'SELECT MAX(prekey_id) FROM signed_prekeys'
- result = self._con.execute(query).fetchone()
- return result.max_prekey_id if result is not None else None
-
- def getSignedPreKeyTimestamp(self, signedPreKeyId):
- query = '''SELECT strftime('%s', timestamp) FROM
- signed_prekeys WHERE prekey_id = ?'''
-
- result = self._con.execute(query, (signedPreKeyId,)).fetchone()
- if result is None:
- raise InvalidKeyIdException('No such signedprekeyrecord! %s' %
- signedPreKeyId)
-
- return result.formated_time
-
- def removeOldSignedPreKeys(self, timestamp):
- query = '''DELETE FROM signed_prekeys
- WHERE timestamp < datetime(?, "unixepoch")'''
- self._con.execute(query, (timestamp,))
- self._con.commit()
-
- def loadSession(self, recipientId, deviceId):
- query = '''SELECT record as "record [session_record]"
- FROM sessions WHERE recipient_id = ? AND device_id = ?'''
- result = self._con.execute(query, (recipientId, deviceId)).fetchone()
- return result.record if result is not None else SessionRecord()
-
- def getJidFromDevice(self, device_id):
- query = '''SELECT recipient_id
- FROM sessions WHERE device_id = ?'''
- result = self._con.execute(query, (device_id, )).fetchone()
- return result.recipient_id if result is not None else None
-
- def getActiveDeviceTuples(self):
- query = '''SELECT recipient_id, device_id
- FROM sessions WHERE active = 1'''
- return self._con.execute(query).fetchall()
-
- def storeSession(self, recipientId, deviceId, sessionRecord):
- query = '''INSERT INTO sessions(recipient_id, device_id, record)
- VALUES(?,?,?)'''
- try:
- self._con.execute(query, (recipientId,
- deviceId,
- sessionRecord.serialize()))
- except sqlite3.IntegrityError:
- query = '''UPDATE sessions SET record = ?
- WHERE recipient_id = ? AND device_id = ?'''
- self._con.execute(query, (sessionRecord.serialize(),
- recipientId,
- deviceId))
-
- self._con.commit()
-
- def containsSession(self, recipientId, deviceId):
- query = '''SELECT record FROM sessions
- WHERE recipient_id = ? AND device_id = ?'''
- result = self._con.execute(query, (recipientId, deviceId)).fetchone()
- return result is not None
-
- def deleteSession(self, recipientId, deviceId):
- self._log.info('Delete session for %s %s', recipientId, deviceId)
- query = "DELETE FROM sessions WHERE recipient_id = ? AND device_id = ?"
- self._con.execute(query, (recipientId, deviceId))
- self._con.commit()
-
- def deleteAllSessions(self, recipientId):
- query = 'DELETE FROM sessions WHERE recipient_id = ?'
- self._con.execute(query, (recipientId,))
- self._con.commit()
-
- def getSessionsFromJid(self, recipientId):
- query = '''SELECT recipient_id,
- device_id,
- record as "record [session_record]",
- active
- FROM sessions WHERE recipient_id = ?'''
- return self._con.execute(query, (recipientId,)).fetchall()
-
- def getSessionsFromJids(self, recipientIds):
- query = '''SELECT recipient_id,
- device_id,
- record as "record [session_record]",
- active
- FROM sessions
- WHERE recipient_id IN ({})'''.format(
- ', '.join(['?'] * len(recipientIds)))
- return self._con.execute(query, recipientIds).fetchall()
-
- def setActiveState(self, jid, devicelist):
- query = '''UPDATE sessions SET active = 1
- WHERE recipient_id = ? AND device_id IN ({})'''.format(
- ', '.join(['?'] * len(devicelist)))
- self._con.execute(query, (jid,) + tuple(devicelist))
-
- query = '''UPDATE sessions SET active = 0
- WHERE recipient_id = ? AND device_id NOT IN ({})'''.format(
- ', '.join(['?'] * len(devicelist)))
- self._con.execute(query, (jid,) + tuple(devicelist))
- self._con.commit()
-
- def setInactive(self, jid, device_id):
- query = '''UPDATE sessions SET active = 0
- WHERE recipient_id = ? AND device_id = ?'''
- self._con.execute(query, (jid, device_id))
- self._con.commit()
-
- def getInactiveSessionsKeys(self, recipientId):
- query = '''SELECT record as "record [session_record]" FROM sessions
- WHERE active = 0 AND recipient_id = ?'''
- results = self._con.execute(query, (recipientId,)).fetchall()
-
- keys = []
- for result in results:
- key = result.record.getSessionState().getRemoteIdentityKey()
- keys.append(IdentityKeyExtended(key.getPublicKey()))
- return keys
-
- def loadPreKey(self, preKeyId):
- query = '''SELECT record FROM prekeys WHERE prekey_id = ?'''
-
- result = self._con.execute(query, (preKeyId,)).fetchone()
- if result is None:
- raise Exception("No such prekeyRecord!")
- return PreKeyRecord(serialized=result.record)
-
- def loadPendingPreKeys(self):
- query = '''SELECT record FROM prekeys'''
- result = self._con.execute(query).fetchall()
- return [PreKeyRecord(serialized=row.record) for row in result]
-
- def storePreKey(self, preKeyId, preKeyRecord):
- query = 'INSERT INTO prekeys (prekey_id, record) VALUES(?,?)'
- self._con.execute(query, (preKeyId, preKeyRecord.serialize()))
- self._con.commit()
-
- def containsPreKey(self, preKeyId):
- query = 'SELECT record FROM prekeys WHERE prekey_id = ?'
- result = self._con.execute(query, (preKeyId,)).fetchone()
- return result is not None
-
- def removePreKey(self, preKeyId):
- query = 'DELETE FROM prekeys WHERE prekey_id = ?'
- self._con.execute(query, (preKeyId,))
- self._con.commit()
-
- def getCurrentPreKeyId(self):
- query = 'SELECT MAX(prekey_id) FROM prekeys'
- return self._con.execute(query).fetchone().max_prekey_id
-
- def getPreKeyCount(self):
- query = 'SELECT COUNT(prekey_id) FROM prekeys'
- return self._con.execute(query).fetchone().count_prekey_id
-
- def generateNewPreKeys(self, count):
- prekey_id = self.getCurrentPreKeyId() or 0
- pre_keys = KeyHelper.generatePreKeys(prekey_id + 1, count)
- for pre_key in pre_keys:
- self.storePreKey(pre_key.getId(), pre_key)
-
- def getIdentityKeyPair(self):
- query = '''SELECT public_key as "public_key [pk]", private_key
- FROM secret LIMIT 1'''
- result = self._con.execute(query).fetchone()
-
- return IdentityKeyPair(result.public_key,
- DjbECPrivateKey(result.private_key))
-
- def getLocalRegistrationId(self):
- query = 'SELECT device_id FROM secret LIMIT 1'
- result = self._con.execute(query).fetchone()
- return result.device_id if result is not None else None
-
- def storeLocalData(self, device_id, identityKeyPair):
- query = 'SELECT * FROM secret'
- result = self._con.execute(query).fetchone()
- if result is not None:
- self._log.error('Trying to save secret key into '
- 'non-empty secret table')
- return
-
- query = '''INSERT INTO secret(device_id, public_key, private_key)
- VALUES(?, ?, ?)'''
-
- public_key = identityKeyPair.getPublicKey().getPublicKey().serialize()
- private_key = identityKeyPair.getPrivateKey().serialize()
- self._con.execute(query, (device_id, public_key, private_key))
- self._con.commit()
-
- def saveIdentity(self, recipientId, identityKey):
- query = '''INSERT INTO identities (recipient_id, public_key, trust, shown)
- VALUES(?, ?, ?, ?)'''
- if not self.containsIdentity(recipientId, identityKey):
- trust = self.getDefaultTrust(recipientId)
- self._con.execute(query, (recipientId,
- identityKey.getPublicKey().serialize(),
- trust,
- 1 if trust == Trust.BLIND else 0))
- self._con.commit()
-
- def containsIdentity(self, recipientId, identityKey):
- query = '''SELECT * FROM identities WHERE recipient_id = ?
- AND public_key = ?'''
-
- public_key = identityKey.getPublicKey().serialize()
- result = self._con.execute(query, (recipientId,
- public_key)).fetchone()
-
- return result is not None
-
- def deleteIdentity(self, recipientId, identityKey):
- query = '''DELETE FROM identities
- WHERE recipient_id = ? AND public_key = ?'''
- public_key = identityKey.getPublicKey().serialize()
- self._con.execute(query, (recipientId, public_key))
- self._con.commit()
-
- def isTrustedIdentity(self, recipientId, identityKey):
- return True
-
- def getTrustForIdentity(self, recipientId, identityKey):
- query = '''SELECT trust FROM identities WHERE recipient_id = ?
- AND public_key = ?'''
- public_key = identityKey.getPublicKey().serialize()
- result = self._con.execute(query, (recipientId, public_key)).fetchone()
- return result.trust if result is not None else None
-
- def getFingerprints(self, jid):
- query = '''SELECT recipient_id,
- public_key as "public_key [pk]",
- trust,
- timestamp
- FROM identities
- WHERE recipient_id = ? ORDER BY trust ASC'''
- return self._con.execute(query, (jid,)).fetchall()
-
- def getMucFingerprints(self, jids):
- query = '''
- SELECT recipient_id,
- public_key as "public_key [pk]",
- trust,
- timestamp
- FROM identities
- WHERE recipient_id IN ({}) ORDER BY trust ASC
- '''.format(', '.join(['?'] * len(jids)))
-
- return self._con.execute(query, jids).fetchall()
-
- def hasUndecidedFingerprints(self, jid):
- query = '''SELECT public_key as "public_key [pk]" FROM identities
- WHERE recipient_id = ? AND trust = ?'''
- result = self._con.execute(query, (jid, Trust.UNDECIDED)).fetchall()
- undecided = [row.public_key for row in result]
-
- inactive = self.getInactiveSessionsKeys(jid)
- undecided = set(undecided) - set(inactive)
- return bool(undecided)
-
- def getDefaultTrust(self, jid):
- if not self._is_blind_trust_enabled():
- return Trust.UNDECIDED
-
- query = '''SELECT * FROM identities
- WHERE recipient_id = ? AND trust IN (0, 1)'''
- result = self._con.execute(query, (jid,)).fetchone()
- if result is None:
- return Trust.BLIND
- return Trust.UNDECIDED
-
- def getTrustedFingerprints(self, jid):
- query = '''SELECT public_key as "public_key [pk]" FROM identities
- WHERE recipient_id = ? AND trust IN(1, 3)'''
- result = self._con.execute(query, (jid,)).fetchall()
- return [row.public_key for row in result]
-
- def getNewFingerprints(self, jid):
- query = '''SELECT _id FROM identities WHERE shown = 0
- AND recipient_id = ?'''
-
- result = self._con.execute(query, (jid,)).fetchall()
- return [row.id for row in result]
-
- def setShownFingerprints(self, fingerprints):
- query = 'UPDATE identities SET shown = 1 WHERE _id IN ({})'.format(
- ', '.join(['?'] * len(fingerprints)))
- self._con.execute(query, fingerprints)
- self._con.commit()
-
- def setTrust(self, recipient_id, identityKey, trust):
- query = '''UPDATE identities SET trust = ? WHERE public_key = ?
- AND recipient_id = ?'''
- public_key = identityKey.getPublicKey().serialize()
- self._con.execute(query, (trust, public_key, recipient_id))
- self._con.commit()
-
- def isTrusted(self, recipient_id, device_id):
- record = self.loadSession(recipient_id, device_id)
- if record.isFresh():
- return False
- identity_key = record.getSessionState().getRemoteIdentityKey()
- return self.getTrustForIdentity(
- recipient_id, identity_key) in (Trust.VERIFIED, Trust.BLIND)
-
- def getIdentityLastSeen(self, recipient_id, identity_key):
- identity_key = identity_key.getPublicKey().serialize()
- query = '''SELECT timestamp FROM identities
- WHERE recipient_id = ? AND public_key = ?'''
- result = self._con.execute(query, (recipient_id,
- identity_key)).fetchone()
- return result.timestamp if result is not None else None
-
- def setIdentityLastSeen(self, recipient_id, identity_key):
- timestamp = int(time.time())
- identity_key = identity_key.getPublicKey().serialize()
- self._log.info('Set last seen for %s %s', recipient_id, timestamp)
- query = '''UPDATE identities SET timestamp = ?
- WHERE recipient_id = ? AND public_key = ?'''
- self._con.execute(query, (timestamp, recipient_id, identity_key))
- self._con.commit()
-
- def getUnacknowledgedCount(self, recipient_id, device_id):
- record = self.loadSession(recipient_id, device_id)
- if record.isFresh():
- return 0
- state = record.getSessionState()
- return state.getSenderChainKey().getIndex()
diff --git a/omemo/backend/state.py b/omemo/backend/state.py
deleted file mode 100644
index ce3b099..0000000
--- a/omemo/backend/state.py
+++ /dev/null
@@ -1,362 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-import time
-from collections import defaultdict
-
-from nbxmpp.structs import OMEMOBundle
-from nbxmpp.structs import OMEMOMessage
-
-from axolotl.ecc.djbec import DjbECPublicKey
-from axolotl.identitykey import IdentityKey
-
-from axolotl.protocol.prekeywhispermessage import PreKeyWhisperMessage
-from axolotl.protocol.whispermessage import WhisperMessage
-from axolotl.sessionbuilder import SessionBuilder
-from axolotl.sessioncipher import SessionCipher
-from axolotl.state.prekeybundle import PreKeyBundle
-from axolotl.util.keyhelper import KeyHelper
-from axolotl.duplicatemessagexception import DuplicateMessageException
-
-from omemo.backend.aes import aes_decrypt
-from omemo.backend.aes import aes_encrypt
-from omemo.backend.aes import get_new_key
-from omemo.backend.aes import get_new_iv
-from omemo.backend.devices import DeviceManager
-from omemo.backend.devices import NoDevicesFound
-from omemo.backend.liteaxolotlstore import LiteAxolotlStore
-from omemo.backend.util import get_fingerprint
-from omemo.backend.util import Trust
-from omemo.backend.util import DEFAULT_PREKEY_AMOUNT
-from omemo.backend.util import MIN_PREKEY_AMOUNT
-from omemo.backend.util import SPK_CYCLE_TIME
-from omemo.backend.util import SPK_ARCHIVE_TIME
-from omemo.backend.util import UNACKNOWLEDGED_COUNT
-
-
-class OmemoState(DeviceManager):
- def __init__(self, own_jid, db_path, account, xmpp_con):
- self._account = account
- self._own_jid = own_jid
- self._log = xmpp_con._log
- self._session_ciphers = defaultdict(dict)
- self._storage = LiteAxolotlStore(db_path, self._log)
-
- DeviceManager.__init__(self)
-
- self.xmpp_con = xmpp_con
-
- self._log.info('%s PreKeys available',
- self._storage.getPreKeyCount())
-
- def build_session(self, jid, device_id, bundle):
- session = SessionBuilder(self._storage, self._storage, self._storage,
- self._storage, jid, device_id)
-
- registration_id = self._storage.getLocalRegistrationId()
-
- prekey = bundle.pick_prekey()
- otpk = DjbECPublicKey(prekey['key'][1:])
-
- spk = DjbECPublicKey(bundle.spk['key'][1:])
- ik = IdentityKey(DjbECPublicKey(bundle.ik[1:]))
-
- prekey_bundle = PreKeyBundle(registration_id,
- device_id,
- prekey['id'],
- otpk,
- bundle.spk['id'],
- spk,
- bundle.spk_signature,
- ik)
-
- session.processPreKeyBundle(prekey_bundle)
- self._get_session_cipher(jid, device_id)
-
- @property
- def storage(self):
- return self._storage
-
- @property
- def own_fingerprint(self):
- return get_fingerprint(self._storage.getIdentityKeyPair())
-
- @property
- def bundle(self):
- self._check_pre_key_count()
-
- bundle = {'otpks': []}
- for k in self._storage.loadPendingPreKeys():
- key = k.getKeyPair().getPublicKey().serialize()
- bundle['otpks'].append({'key': key, 'id': k.getId()})
-
- ik_pair = self._storage.getIdentityKeyPair()
- bundle['ik'] = ik_pair.getPublicKey().serialize()
-
- self._cycle_signed_pre_key(ik_pair)
-
- spk = self._storage.loadSignedPreKey(
- self._storage.getCurrentSignedPreKeyId())
- bundle['spk_signature'] = spk.getSignature()
- bundle['spk'] = {'key': spk.getKeyPair().getPublicKey().serialize(),
- 'id': spk.getId()}
-
- return OMEMOBundle(**bundle)
-
- def decrypt_message(self, omemo_message, jid):
- if omemo_message.sid == self.own_device:
- self._log.info('Received previously sent message by us')
- raise SelfMessage
-
- try:
- encrypted_key, prekey = omemo_message.keys[self.own_device]
- except KeyError:
- self._log.info('Received message not for our device')
- raise MessageNotForDevice
-
- try:
- if prekey:
- key, fingerprint, trust = self._process_pre_key_message(
- jid, omemo_message.sid, encrypted_key)
- else:
- key, fingerprint, trust = self._process_message(
- jid, omemo_message.sid, encrypted_key)
-
- except DuplicateMessageException:
- self._log.info('Received duplicated message')
- raise DuplicateMessage
-
- except Exception as error:
- self._log.warning(error)
- raise DecryptionFailed
-
- if omemo_message.payload is None:
- self._log.debug("Decrypted Key Exchange Message")
- raise KeyExchangeMessage
-
- try:
- result = aes_decrypt(key, omemo_message.iv, omemo_message.payload)
- except Exception as error:
- self._log.warning(error)
- raise DecryptionFailed
-
- self._log.debug("Decrypted Message => %s", result)
- return result, fingerprint, trust
-
- def _get_whisper_message(self, jid, device, key):
- cipher = self._get_session_cipher(jid, device)
- cipher_key = cipher.encrypt(key)
- prekey = isinstance(cipher_key, PreKeyWhisperMessage)
- return cipher_key.serialize(), prekey
-
- def encrypt(self, jid, plaintext):
- try:
- devices_for_encryption = self.get_devices_for_encryption(jid)
- except NoDevicesFound:
- self._log.warning('No devices for encryption found for: %s', jid)
- return
-
- result = aes_encrypt(plaintext)
- whisper_messages = defaultdict(dict)
-
- for jid_, device in devices_for_encryption:
- count = self._storage.getUnacknowledgedCount(jid_, device)
- if count >= UNACKNOWLEDGED_COUNT:
- self._log.warning('Set device inactive %s because of %s '
- 'unacknowledged messages', device, count)
- self.remove_device(jid_, device)
-
- try:
- whisper_messages[jid_][device] = self._get_whisper_message(
- jid_, device, result.key)
- except Exception:
- self._log.exception('Failed to encrypt')
- continue
-
- recipients = set(whisper_messages.keys())
- if jid != self._own_jid:
- recipients -= set([self._own_jid])
- if not recipients:
- self._log.error('Encrypted keys empty')
- return
-
- encrypted_keys = {}
- for jid_ in whisper_messages:
- encrypted_keys.update(whisper_messages[jid_])
-
- self._log.debug('Finished encrypting message')
- return OMEMOMessage(sid=self.own_device,
- keys=encrypted_keys,
- iv=result.iv,
- payload=result.payload)
-
- def encrypt_key_transport(self, jid, devices):
- whisper_messages = defaultdict(dict)
- for device in devices:
- try:
- whisper_messages[jid][device] = self._get_whisper_message(
- jid, device, get_new_key())
- except Exception:
- self._log.exception('Failed to encrypt')
- continue
-
- if not whisper_messages[jid]:
- self._log.error('Encrypted keys empty')
- return
-
- self._log.debug('Finished Key Transport message')
- return OMEMOMessage(sid=self.own_device,
- keys=whisper_messages[jid],
- iv=get_new_iv(),
- payload=None)
-
- def has_trusted_keys(self, jid):
- inactive = self._storage.getInactiveSessionsKeys(jid)
- trusted = self._storage.getTrustedFingerprints(jid)
- return bool(set(trusted) - set(inactive))
-
- def devices_without_sessions(self, jid):
- known_devices = self.get_devices(jid, without_self=True)
- missing_devices = [dev
- for dev in known_devices
- if not self._storage.containsSession(jid, dev)]
- if missing_devices:
- self._log.info('Missing device sessions for %s: %s',
- jid, missing_devices)
- return missing_devices
-
- def _get_session_cipher(self, jid, device_id):
- try:
- return self._session_ciphers[jid][device_id]
- except KeyError:
- cipher = SessionCipher(self._storage, self._storage, self._storage,
- self._storage, jid, device_id)
- self._session_ciphers[jid][device_id] = cipher
- return cipher
-
- def _process_pre_key_message(self, jid, device, key):
- self._log.info('Process pre key message from %s', jid)
- pre_key_message = PreKeyWhisperMessage(serialized=key)
- if not pre_key_message.getPreKeyId():
- raise Exception('Received Pre Key Message '
- 'without PreKey => %s' % jid)
-
- session_cipher = self._get_session_cipher(jid, device)
- key = session_cipher.decryptPkmsg(pre_key_message)
-
- identity_key = pre_key_message.getIdentityKey()
- trust = self._get_trust_from_identity_key(jid, identity_key)
- fingerprint = get_fingerprint(identity_key)
-
- self._storage.setIdentityLastSeen(jid, identity_key)
-
- self.xmpp_con.set_bundle()
- self.add_device(jid, device)
- return key, fingerprint, trust
-
- def _process_message(self, jid, device, key):
- self._log.info('Process message from %s', jid)
- message = WhisperMessage(serialized=key)
-
- session_cipher = self._get_session_cipher(jid, device)
- key = session_cipher.decryptMsg(message, textMsg=False)
-
- identity_key = self._get_identity_key_from_device(jid, device)
- trust = self._get_trust_from_identity_key(jid, identity_key)
- fingerprint = get_fingerprint(identity_key)
-
- self._storage.setIdentityLastSeen(jid, identity_key)
-
- self.add_device(jid, device)
-
- return key, fingerprint, trust
-
- @staticmethod
- def _get_identity_key_from_pk_message(key):
- pre_key_message = PreKeyWhisperMessage(serialized=key)
- return pre_key_message.getIdentityKey()
-
- def _get_identity_key_from_device(self, jid, device):
- session_record = self._storage.loadSession(jid, device)
- return session_record.getSessionState().getRemoteIdentityKey()
-
- def _get_trust_from_identity_key(self, jid, identity_key):
- trust = self._storage.getTrustForIdentity(jid, identity_key)
- return Trust(trust) if trust is not None else Trust.UNDECIDED
-
- def _check_pre_key_count(self):
- # Check if enough PreKeys are available
- pre_key_count = self._storage.getPreKeyCount()
- if pre_key_count < MIN_PREKEY_AMOUNT:
- missing_count = DEFAULT_PREKEY_AMOUNT - pre_key_count
- self._storage.generateNewPreKeys(missing_count)
- self._log.info('%s PreKeys created', missing_count)
-
- def _cycle_signed_pre_key(self, ik_pair):
- # Publish every SPK_CYCLE_TIME a new SignedPreKey
- # Delete all exsiting SignedPreKeys that are older
- # then SPK_ARCHIVE_TIME
-
- # Check if SignedPreKey exist and create if not
- if not self._storage.getCurrentSignedPreKeyId():
- spk = KeyHelper.generateSignedPreKey(
- ik_pair, self._storage.getNextSignedPreKeyId())
- self._storage.storeSignedPreKey(spk.getId(), spk)
- self._log.debug('New SignedPreKey created, because none existed')
-
- # if SPK_CYCLE_TIME is reached, generate a new SignedPreKey
- now = int(time.time())
- timestamp = self._storage.getSignedPreKeyTimestamp(
- self._storage.getCurrentSignedPreKeyId())
-
- if int(timestamp) < now - SPK_CYCLE_TIME:
- spk = KeyHelper.generateSignedPreKey(
- ik_pair, self._storage.getNextSignedPreKeyId())
- self._storage.storeSignedPreKey(spk.getId(), spk)
- self._log.debug('Cycled SignedPreKey')
-
- # Delete all SignedPreKeys that are older than SPK_ARCHIVE_TIME
- timestamp = now - SPK_ARCHIVE_TIME
- self._storage.removeOldSignedPreKeys(timestamp)
-
-
-class NoValidSessions(Exception):
- pass
-
-
-class SelfMessage(Exception):
- pass
-
-
-class MessageNotForDevice(Exception):
- pass
-
-
-class DecryptionFailed(Exception):
- pass
-
-
-class KeyExchangeMessage(Exception):
- pass
-
-
-class InvalidMessage(Exception):
- pass
-
-
-class DuplicateMessage(Exception):
- pass
diff --git a/omemo/backend/util.py b/omemo/backend/util.py
deleted file mode 100644
index 9d07d5f..0000000
--- a/omemo/backend/util.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-import binascii
-import textwrap
-from enum import IntEnum
-
-from axolotl.identitykey import IdentityKey
-
-DEFAULT_PREKEY_AMOUNT = 100
-MIN_PREKEY_AMOUNT = 80
-SPK_ARCHIVE_TIME = 86400 * 15 # 15 Days
-SPK_CYCLE_TIME = 86400 # 24 Hours
-UNACKNOWLEDGED_COUNT = 2000
-
-
-class Trust(IntEnum):
- UNTRUSTED = 0
- VERIFIED = 1
- UNDECIDED = 2
- BLIND = 3
-
-
-def get_fingerprint(identity_key, formatted=False):
- public_key = identity_key.getPublicKey().serialize()
- fingerprint = binascii.hexlify(public_key).decode()[2:]
- if not formatted:
- return fingerprint
- fplen = len(fingerprint)
- wordsize = fplen // 8
- buf = ''
- for w in range(0, fplen, wordsize):
- buf += '{0} '.format(fingerprint[w:w + wordsize])
- buf = textwrap.fill(buf, width=36)
- return buf.rstrip().upper()
-
-
-class IdentityKeyExtended(IdentityKey):
- def __hash__(self):
- return hash(self.publicKey.serialize())
-
- def get_fingerprint(self, formatted=False):
- return get_fingerprint(self, formatted=formatted)
diff --git a/omemo/gtk/__init__.py b/omemo/gtk/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/omemo/gtk/__init__.py
+++ /dev/null
diff --git a/omemo/gtk/config.py b/omemo/gtk/config.py
deleted file mode 100644
index 5543c35..0000000
--- a/omemo/gtk/config.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
-# Copyright (C) 2015 Daniel Gultsch <daniel@cgultsch.de>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-import logging
-
-from gi.repository import Gtk
-from gi.repository import Gdk
-
-from gajim.common import app
-from gajim.plugins.helpers import get_builder
-from gajim.plugins.plugins_i18n import _
-
-from omemo.backend.util import get_fingerprint
-
-log = logging.getLogger('gajim.p.omemo')
-
-
-class OMEMOConfigDialog(Gtk.ApplicationWindow):
- def __init__(self, plugin, transient):
- Gtk.ApplicationWindow.__init__(self)
- self.set_application(app.app)
- self.set_show_menubar(False)
- self.set_title(_('OMEMO Settings'))
- self.set_transient_for(transient)
- self.set_default_size(400, 400)
- self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
- self.set_destroy_with_parent(True)
-
- self._plugin = plugin
-
- path = self._plugin.local_file_path('gtk/config.ui')
- self._ui = get_builder(path)
-
- image_path = self._plugin.local_file_path('omemo.png')
- self._ui.image.set_from_file(image_path)
-
- try:
- self.disabled_accounts = self._plugin.config['DISABLED_ACCOUNTS']
- except KeyError:
- self._plugin.config['DISABLED_ACCOUNTS'] = []
- self.disabled_accounts = self._plugin.config['DISABLED_ACCOUNTS']
-
- box = Gtk.Box()
- box.pack_start(self._ui.notebook1, True, True, 0)
-
- self.add(box)
-
- self._ui.connect_signals(self)
- self.show_all()
-
- self.plugin_active = False
- for plugin in app.plugin_manager.active_plugins:
- log.debug(type(plugin))
- if type(plugin).__name__ == 'OmemoPlugin':
- self.plugin_active = True
- break
-
- self.update_account_store()
- self.update_account_combobox()
- self.update_disabled_account_view()
- self.update_settings()
-
- def is_in_accountstore(self, account):
- for row in self._ui.account_store:
- if row[0] == account:
- return True
- return False
-
- def update_account_store(self):
- for account in sorted(app.settings.get_active_accounts()):
- if account in self.disabled_accounts:
- continue
- if account == 'Local':
- continue
- if not self.is_in_accountstore(account):
- self._ui.account_store.append(row=(account,))
-
- def update_account_combobox(self):
- if self.plugin_active is False:
- return
- if len(self._ui.account_store):
- self._ui.account_combobox.set_active(0)
- else:
- self.account_combobox_changed_cb(self._ui.account_combobox)
-
- def account_combobox_changed_cb(self, box, *args):
- self.update_context_list()
-
- def update_disabled_account_view(self):
- self._ui.disabled_account_store.clear()
- for account in self.disabled_accounts:
- self._ui.disabled_account_store.append(row=(account,))
-
- def activate_accounts_btn_clicked(self, _button, *args):
- selection = self._ui.disabled_accounts_view.get_selection()
- mod, paths = selection.get_selected_rows()
- for path in paths:
- it = mod.get_iter(path)
- account = mod.get(it, 0)
- if account[0] in self.disabled_accounts and \
- not self.is_in_accountstore(account[0]):
- self._ui.account_store.append(row=(account[0],))
- self.disabled_accounts.remove(account[0])
- self.update_disabled_account_view()
- self._plugin.config['DISABLED_ACCOUNTS'] = self.disabled_accounts
- self.update_account_combobox()
-
- def disable_accounts_btn_clicked(self, _button, *args):
- selection = self._ui.active_accounts_view.get_selection()
- mod, paths = selection.get_selected_rows()
- for path in paths:
- it = mod.get_iter(path)
- account = mod.get(it, 0)
- if account[0] not in self.disabled_accounts and \
- self.is_in_accountstore(account[0]):
- self.disabled_accounts.append(account[0])
- self._ui.account_store.remove(it)
- self.update_disabled_account_view()
- self._plugin.config['DISABLED_ACCOUNTS'] = self.disabled_accounts
- self.update_account_combobox()
-
- def cleardevice_button_clicked_cb(self, button, *args):
- active = self._ui.account_combobox.get_active()
- account = self._ui.account_store[active][0]
- app.get_client(account).get_module('OMEMO').clear_devicelist()
- self.update_context_list()
-
- def refresh_button_clicked_cb(self, button, *args):
- self.update_context_list()
-
- def _on_blind_trust(self, button):
- self._plugin.config['BLIND_TRUST'] = button.get_active()
-
- def update_context_list(self):
- self._ui.deviceid_store.clear()
-
- if not len(self._ui.account_store):
- self._ui.ID.set_markup('')
- self._ui.fingerprint_label.set_markup('')
- self._ui.refresh.set_sensitive(False)
- self._ui.cleardevice_button.set_sensitive(False)
- return
- active = self._ui.account_combobox.get_active()
- account = self._ui.account_store[active][0]
-
- # Set buttons active
- self._ui.refresh.set_sensitive(True)
- if account == 'Local':
- self._ui.cleardevice_button.set_sensitive(False)
- else:
- self._ui.cleardevice_button.set_sensitive(True)
-
- # Set FPR Label and DeviceID
- omemo = self._plugin.get_omemo(account)
- self._ui.ID.set_markup('<tt>%s</tt>' % omemo.backend.own_device)
-
- identity_key = omemo.backend.storage.getIdentityKeyPair()
- fpr = get_fingerprint(identity_key, formatted=True)
- self._ui.fingerprint_label.set_markup('<tt>%s</tt>' % fpr)
-
- own_jid = app.get_jid_from_account(account)
- # Set Device ID List
- for item in omemo.backend.get_devices(own_jid):
- self._ui.deviceid_store.append([item])
-
- def update_settings(self):
- self._ui.blind_trust_checkbutton.set_active(
- self._plugin.config['BLIND_TRUST'])
diff --git a/omemo/gtk/config.ui b/omemo/gtk/config.ui
deleted file mode 100644
index d5f991e..0000000
--- a/omemo/gtk/config.ui
+++ /dev/null
@@ -1,614 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
-<interface>
- <requires lib="gtk+" version="3.20"/>
- <object class="GtkListStore" id="account_store">
- <columns>
- <!-- column-name accounts -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkListStore" id="deviceid_store">
- <columns>
- <!-- column-name Device -->
- <column type="gint"/>
- </columns>
- </object>
- <object class="GtkListStore" id="disabled_account_store">
- <columns>
- <!-- column-name accounts -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkNotebook" id="notebook1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">12</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes" comments="label for account selector">Acc_ount</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">account_combobox</property>
- <style>
- <class name="bold"/>
- <class name="dim-label"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBox" id="account_combobox">
- <property name="width_request">200</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="has_focus">True</property>
- <property name="halign">start</property>
- <property name="model">account_store</property>
- <signal name="changed" handler="account_combobox_changed_cb" swapped="no"/>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext1"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="margin_top">6</property>
- <property name="label" translatable="yes" comments="Descriptive label">Own _Fingerprint</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">fingerprint_label</property>
- <style>
- <class name="bold"/>
- <class name="dim-label"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="fingerprint_label">
- <property name="width_request">200</property>
- <property name="height_request">30</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="margin_top">6</property>
- <property name="use_markup">True</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">Own _Device ID</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">ID</property>
- <style>
- <class name="bold"/>
- <class name="dim-label"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="ID">
- <property name="width_request">200</property>
- <property name="height_request">30</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="selectable">True</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Note: Fingerprints of your contacts are managed in the message window.</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">50</property>
- <property name="xalign">0</property>
- <attributes>
- <attribute name="style" value="italic"/>
- </attributes>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="stock">gtk-missing-image</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="tab">
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes" comments="tab label">Own Fingerprints</property>
- </object>
- <packing>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkLabel">
- <property name="height_request">25</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="label" translatable="yes">Published Devices</property>
- <style>
- <class name="bold"/>
- <class name="dim-label"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow2">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="halign">center</property>
- <property name="hscrollbar_policy">never</property>
- <child>
- <object class="GtkTreeView" id="deviceid_view">
- <property name="width_request">300</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="model">deviceid_store</property>
- <property name="search_column">0</property>
- <property name="enable_grid_lines">horizontal</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection2"/>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="deviceid_column">
- <property name="title" translatable="yes">Device ID</property>
- <property name="clickable">True</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext3"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="margin_top">6</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkButton" id="cleardevice_button">
- <property name="label" translatable="yes">_Clear Devices</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">This clears your device list from the server.
-Clearing the device list helps you to remove unused devices from the encryption process.
-It is advised to go online with all of your actively used devices after clearing.</property>
- <property name="use_underline">True</property>
- <signal name="clicked" handler="cleardevice_button_clicked_cb" swapped="no"/>
- <style>
- <class name="destructive-action"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="refresh">
- <property name="label">gtk-refresh</property>
- <property name="width_request">160</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="refresh_button_clicked_cb" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Clear Devices</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkInfoBar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="message_type">warning</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox">
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <property name="layout_style">end</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel">
- <property name="height_request">30</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">You have to restart Gajim for changes to take effect !</property>
- <style>
- <class name="bold"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">12</property>
- <property name="column_homogeneous">True</property>
- <child>
- <object class="GtkScrolledWindow">
- <property name="height_request">200</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="shadow_type">out</property>
- <child>
- <object class="GtkTreeView" id="active_accounts_view">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="vexpand">True</property>
- <property name="model">account_store</property>
- <property name="enable_grid_lines">horizontal</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection3"/>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="treeviewcolumn1">
- <property name="title" translatable="yes">Active Accounts</property>
- <property name="alignment">0.5</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext5"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="shadow_type">out</property>
- <child>
- <object class="GtkTreeView" id="disabled_accounts_view">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="vexpand">True</property>
- <property name="model">disabled_account_store</property>
- <property name="enable_grid_lines">horizontal</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection4"/>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="treeviewcolumn2">
- <property name="title" translatable="yes">Disabled Accounts</property>
- <property name="alignment">0.5</property>
- <child>
- <object class="GtkCellRendererText" id="cellrenderertext6"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="disable_accounts_btn">
- <property name="label" translatable="yes">_Disable Account</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">center</property>
- <property name="use_underline">True</property>
- <signal name="clicked" handler="disable_accounts_btn_clicked" swapped="no"/>
- <style>
- <class name="destructive-action"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="activate_accounts_btn">
- <property name="label" translatable="yes">_Enable Account</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">center</property>
- <property name="use_underline">True</property>
- <signal name="clicked" handler="activate_accounts_btn_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="disable_accounts">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Disable Accounts</property>
- </object>
- <packing>
- <property name="position">2</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">18</property>
- <property name="margin_right">18</property>
- <property name="margin_top">18</property>
- <property name="margin_bottom">18</property>
- <child>
- <object class="GtkFrame">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">none</property>
- <child>
- <object class="GtkAlignment">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="top_padding">12</property>
- <property name="left_padding">12</property>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkCheckButton" id="blind_trust_checkbutton">
- <property name="label" translatable="yes">Blind Trust Before Verification</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="_on_blind_trust" swapped="no"/>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">General</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">3</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Settings</property>
- </object>
- <packing>
- <property name="position">3</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- </object>
- <object class="GtkListStore" id="fingerprint_store">
- <columns>
- <!-- column-name id -->
- <column type="gint"/>
- <!-- column-name screenname -->
- <column type="gchararray"/>
- <!-- column-name trust -->
- <column type="gchararray"/>
- <!-- column-name fingerprint -->
- <column type="gchararray"/>
- <!-- column-name deviceid -->
- <column type="gint"/>
- </columns>
- </object>
-</interface>
diff --git a/omemo/gtk/key.py b/omemo/gtk/key.py
deleted file mode 100644
index e603c80..0000000
--- a/omemo/gtk/key.py
+++ /dev/null
@@ -1,454 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-import time
-import locale
-import logging
-import tempfile
-from packaging.version import Version as V
-
-from pkg_resources import get_distribution
-from gi.repository import Gtk
-from gi.repository import GdkPixbuf
-
-from gajim.common import app
-from gajim.plugins.plugins_i18n import _
-from gajim.plugins.helpers import get_builder
-from gajim.gtk.dialogs import ConfirmationDialog
-from gajim.gtk.dialogs import DialogButton
-
-from omemo.backend.util import Trust
-from omemo.backend.util import IdentityKeyExtended
-from omemo.backend.util import get_fingerprint
-
-log = logging.getLogger('gajim.p.omemo')
-
-
-TRUST_DATA = {
- Trust.UNTRUSTED: ('dialog-error-symbolic',
- _('Untrusted'),
- 'error-color'),
- Trust.UNDECIDED: ('security-low-symbolic',
- _('Not Decided'),
- 'warning-color'),
- Trust.VERIFIED: ('security-high-symbolic',
- _('Verified'),
- 'encrypted-color'),
- Trust.BLIND: ('security-medium-symbolic',
- _('Blind Trust'),
- 'encrypted-color')
-}
-
-
-class KeyDialog(Gtk.Dialog):
- def __init__(self, plugin, contact, transient, windows,
- groupchat=False):
- super().__init__(title=_('OMEMO Fingerprints'),
- destroy_with_parent=True)
-
- self.set_transient_for(transient)
- self.set_resizable(True)
- self.set_default_size(500, 450)
-
- self.get_style_context().add_class('omemo-key-dialog')
-
- self._groupchat = groupchat
- self._contact = contact
- self._windows = windows
- self._account = self._contact.account
- self._plugin = plugin
- self._omemo = self._plugin.get_omemo(self._account)
- self._own_jid = app.get_jid_from_account(self._account)
- self._show_inactive = False
-
- path = self._plugin.local_file_path('gtk/key.ui')
- self._ui = get_builder(path)
-
- markup = '<a href="%s">%s</a>' % (
- 'https://dev.gajim.org/gajim/gajim-plugins/-/'
- 'wikis/omemogajimplugin', _('Read more about blind trust.'))
- self._ui.btbv_link.set_markup(markup)
- self._ui.infobar.set_revealed(
- self._plugin.config['SHOW_HELP_FINGERPRINTS'])
-
- self._ui.header.set_text(_('Fingerprints for %s') % self._contact.jid)
-
- omemo_img_path = self._plugin.local_file_path('omemo.png')
- self._ui.omemo_image.set_from_file(omemo_img_path)
-
- self._ui.list.set_filter_func(self._filter_func, None)
- self._ui.list.set_sort_func(self._sort_func, None)
-
- self._identity_key = self._omemo.backend.storage.getIdentityKeyPair()
- ownfpr_format = get_fingerprint(self._identity_key, formatted=True)
- self._ui.own_fingerprint.set_text(ownfpr_format)
-
- self.get_content_area().add(self._ui.box)
-
- self.update()
- self._load_qrcode()
- self._ui.connect_signals(self)
- self.connect('destroy', self._on_destroy)
- self.show_all()
-
- def _on_infobar_response(self, _widget, response):
- if response == Gtk.ResponseType.CLOSE:
- self._ui.infobar.set_revealed(False)
- self._plugin.config['SHOW_HELP_FINGERPRINTS'] = False
-
- def _filter_func(self, row, _user_data):
- search_text = self._ui.search.get_text()
- if search_text and search_text.lower() not in str(row.jid):
- return False
- if self._show_inactive:
- return True
- return row.active
-
- @staticmethod
- def _sort_func(row1, row2, _user_data):
- result = locale.strcoll(str(row1.jid), str(row2.jid))
- if result != 0:
- return result
-
- if row1.active != row2.active:
- return -1 if row1.active else 1
-
- if row1.trust != row2.trust:
- return -1 if row1.trust > row2.trust else 1
- return 0
-
- def _on_search_changed(self, _entry):
- self._ui.list.invalidate_filter()
-
- def update(self):
- self._ui.list.foreach(self._ui.list.remove)
- self._load_fingerprints(self._own_jid)
- self._load_fingerprints(self._contact.jid, self._groupchat is True)
-
- def _load_fingerprints(self, contact_jid, groupchat=False):
- if groupchat:
- members = list(self._omemo.backend.get_muc_members(contact_jid))
- sessions = self._omemo.backend.storage.getSessionsFromJids(members)
- else:
- sessions = self._omemo.backend.storage.getSessionsFromJid(contact_jid)
-
- rows = {}
- if groupchat:
- results = self._omemo.backend.storage.getMucFingerprints(members)
- else:
- results = self._omemo.backend.storage.getFingerprints(contact_jid)
- for result in results:
- rows[result.public_key] = KeyRow(result.recipient_id,
- result.public_key,
- result.trust,
- result.timestamp)
-
- for item in sessions:
- if item.record.isFresh():
- return
- identity_key = item.record.getSessionState().getRemoteIdentityKey()
- identity_key = IdentityKeyExtended(identity_key.getPublicKey())
- try:
- key_row = rows[identity_key]
- except KeyError:
- log.warning('Could not find session identitykey %s',
- item.device_id)
- self._omemo.backend.storage.deleteSession(item.recipient_id,
- item.device_id)
- continue
-
- key_row.active = item.active
- key_row.device_id = item.device_id
-
- for row in rows.values():
- self._ui.list.add(row)
-
- @staticmethod
- def _get_qrcode(jid, sid, identity_key):
- fingerprint = get_fingerprint(identity_key)
- path = os.path.join(tempfile.gettempdir(),
- 'omemo_{}.png'.format(jid))
-
- ver_string = 'xmpp:{}?omemo-sid-{}={}'.format(jid, sid, fingerprint)
- log.debug('Verification String: %s', ver_string)
-
- import qrcode
- qr = qrcode.QRCode(version=None,
- error_correction=qrcode.constants.ERROR_CORRECT_L,
- box_size=6,
- border=4)
- qr.add_data(ver_string)
- qr.make(fit=True)
-
- fill_color = 'black'
- back_color = 'white'
- if V(get_distribution('qrcode').version) < V('6.0'):
- # meaning of fill_color and back_color were switched
- # before this commit in qrcode between versions 5.3
- # and 6.0: https://github.com/lincolnloop/python-qrcode/
- # commit/01f440d64b7d1f61bb75161ce118b86eca85b15c
- back_color, fill_color = fill_color, back_color
-
- img = qr.make_image(fill_color=fill_color, back_color=back_color)
- img.save(path)
- return path
-
- def _load_qrcode(self):
- try:
- path = self._get_qrcode(self._own_jid,
- self._omemo.backend.own_device,
- self._identity_key)
- except ImportError:
- log.exception('Failed to generate QR code')
- self._ui.qrcode.hide()
- self._ui.qrinfo.show()
- else:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
- self._ui.qrcode.set_from_pixbuf(pixbuf)
- self._ui.qrcode.show()
- self._ui.qrinfo.hide()
-
- def _on_show_inactive(self, switch, param):
- self._show_inactive = switch.get_active()
- self._ui.list.invalidate_filter()
-
- def _on_destroy(self, *args):
- del self._windows['dialog']
-
-
-class KeyRow(Gtk.ListBoxRow):
- def __init__(self, jid, identity_key, trust, last_seen):
- Gtk.ListBoxRow.__init__(self)
- self.set_activatable(False)
-
- self._active = False
- self._device_id = None
- self._identity_key = identity_key
- self.trust = trust
- self.jid = jid
-
- grid = Gtk.Grid()
- grid.set_column_spacing(12)
-
- self._trust_button = TrustButton(self)
- grid.attach(self._trust_button, 1, 1, 1, 3)
-
- jid_label = Gtk.Label(label=jid)
- jid_label.get_style_context().add_class('dim-label')
- jid_label.set_selectable(False)
- jid_label.set_halign(Gtk.Align.START)
- jid_label.set_valign(Gtk.Align.START)
- jid_label.set_hexpand(True)
- grid.attach(jid_label, 2, 1, 1, 1)
-
- self.fingerprint = Gtk.Label(
- label=self._identity_key.get_fingerprint(formatted=True))
- self.fingerprint.get_style_context().add_class('omemo-mono')
- self.fingerprint.get_style_context().add_class('omemo-inactive-color')
- self.fingerprint.set_selectable(True)
- self.fingerprint.set_halign(Gtk.Align.START)
- self.fingerprint.set_valign(Gtk.Align.START)
- self.fingerprint.set_hexpand(True)
- grid.attach(self.fingerprint, 2, 2, 1, 1)
-
- if last_seen is not None:
- last_seen = time.strftime('%d-%m-%Y %H:%M:%S',
- time.localtime(last_seen))
- else:
- last_seen = _('Never')
- last_seen_label = Gtk.Label(label=_('Last seen: %s') % last_seen)
- last_seen_label.set_halign(Gtk.Align.START)
- last_seen_label.set_valign(Gtk.Align.START)
- last_seen_label.set_hexpand(True)
- last_seen_label.get_style_context().add_class('omemo-last-seen')
- last_seen_label.get_style_context().add_class('dim-label')
- grid.attach(last_seen_label, 2, 3, 1, 1)
-
- self.add(grid)
- self.show_all()
-
- def delete_fingerprint(self, *args):
- def _remove():
- backend = self.get_toplevel()._omemo.backend
-
- backend.remove_device(self.jid, self.device_id)
- backend.storage.deleteSession(self.jid, self.device_id)
- backend.storage.deleteIdentity(self.jid, self._identity_key)
-
- self.get_parent().remove(self)
- self.destroy()
-
- ConfirmationDialog(
- _('Delete'),
- _('Delete Fingerprint'),
- _('Doing so will permanently delete this Fingerprint'),
- [DialogButton.make('Cancel'),
- DialogButton.make('Remove',
- text=_('Delete'),
- callback=_remove)],
- transient_for=self.get_toplevel()).show()
-
- def set_trust(self):
- icon_name, tooltip, css_class = TRUST_DATA[self.trust]
- image = self._trust_button.get_child()
- image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
- image.get_style_context().add_class(css_class)
- image.set_tooltip_text(tooltip)
-
- backend = self.get_toplevel()._omemo.backend
- backend.storage.setTrust(self.jid, self._identity_key, self.trust)
-
- @property
- def active(self):
- return self._active
-
- @active.setter
- def active(self, active):
- context = self.fingerprint.get_style_context()
- self._active = bool(active)
- if self._active:
- context.remove_class('omemo-inactive-color')
- else:
- context.add_class('omemo-inactive-color')
- self._trust_button.update()
-
- @property
- def device_id(self):
- return self._device_id
-
- @device_id.setter
- def device_id(self, device_id):
- self._device_id = device_id
-
-
-class TrustButton(Gtk.MenuButton):
- def __init__(self, row):
- Gtk.MenuButton.__init__(self)
- self._row = row
- self._css_class = ''
- self.set_popover(TrustPopver(row))
- self.set_valign(Gtk.Align.CENTER)
- self.update()
-
- def update(self):
- icon_name, tooltip, css_class = TRUST_DATA[self._row.trust]
- image = self.get_child()
- image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
- # Remove old color from icon
- image.get_style_context().remove_class(self._css_class)
-
- if not self._row.active:
- css_class = 'omemo-inactive-color'
- tooltip = '%s - %s' % (_('Inactive'), tooltip)
-
- image.get_style_context().add_class(css_class)
- self._css_class = css_class
- self.set_tooltip_text(tooltip)
-
-
-class TrustPopver(Gtk.Popover):
- def __init__(self, row):
- Gtk.Popover.__init__(self)
- self._row = row
- self._listbox = Gtk.ListBox()
- self._listbox.set_selection_mode(Gtk.SelectionMode.NONE)
- self.update()
- self.add(self._listbox)
- self._listbox.show_all()
- self._listbox.connect('row-activated', self._activated)
- self.get_style_context().add_class('omemo-trust-popover')
-
- def _activated(self, _listbox, row):
- self.popdown()
- if row.type_ is None:
- self._row.delete_fingerprint()
- else:
- self._row.trust = row.type_
- self._row.set_trust()
- self.get_relative_to().update()
- self.update()
-
- def update(self):
- self._listbox.foreach(self._listbox.remove)
- if self._row.trust != Trust.VERIFIED:
- self._listbox.add(VerifiedOption())
- if self._row.trust != Trust.BLIND:
- self._listbox.add(BlindOption())
- if self._row.trust != Trust.UNTRUSTED:
- self._listbox.add(NotTrustedOption())
- self._listbox.add(DeleteOption())
-
-
-class MenuOption(Gtk.ListBoxRow):
- def __init__(self):
- Gtk.ListBoxRow.__init__(self)
- box = Gtk.Box()
- box.set_spacing(6)
-
- image = Gtk.Image.new_from_icon_name(self.icon,
- Gtk.IconSize.MENU)
- label = Gtk.Label(label=self.label)
- image.get_style_context().add_class(self.color)
-
- box.add(image)
- box.add(label)
- self.add(box)
- self.show_all()
-
-
-class BlindOption(MenuOption):
-
- type_ = Trust.BLIND
- icon = 'security-medium-symbolic'
- label = _('Blind Trust')
- color = 'encrypted-color'
-
- def __init__(self):
- MenuOption.__init__(self)
-
-
-class VerifiedOption(MenuOption):
-
- type_ = Trust.VERIFIED
- icon = 'security-high-symbolic'
- label = _('Verified')
- color = 'encrypted-color'
-
- def __init__(self):
- MenuOption.__init__(self)
-
-
-class NotTrustedOption(MenuOption):
-
- type_ = Trust.UNTRUSTED
- icon = 'dialog-error-symbolic'
- label = _('Untrusted')
- color = 'error-color'
-
- def __init__(self):
- MenuOption.__init__(self)
-
-
-class DeleteOption(MenuOption):
-
- type_ = None
- icon = 'user-trash-symbolic'
- label = _('Delete')
- color = ''
-
- def __init__(self):
- MenuOption.__init__(self)
diff --git a/omemo/gtk/key.ui b/omemo/gtk/key.ui
deleted file mode 100644
index 433bac6..0000000
--- a/omemo/gtk/key.ui
+++ /dev/null
@@ -1,332 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.36.0 -->
-<interface>
- <requires lib="gtk+" version="3.22"/>
- <object class="GtkPopover" id="popover">
- <property name="can_focus">False</property>
- <property name="constrain_to">none</property>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">12</property>
- <property name="orientation">vertical</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="own_fingerprint">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="selectable">True</property>
- <style>
- <class name="omemo-mono"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="qrcode">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="qrinfo">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">For verification via QR-Code
-you have to install python-qrcode</property>
- <style>
- <class name="omemo-qr-not-available"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <object class="GtkPopover" id="search_popover">
- <property name="can_focus">False</property>
- <child>
- <object class="GtkSearchEntry" id="search">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="caps_lock_warning">False</property>
- <property name="primary_icon_name">edit-find-symbolic</property>
- <property name="primary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">False</property>
- <signal name="search-changed" handler="_on_search_changed" swapped="no"/>
- </object>
- </child>
- </object>
- <object class="GtkBox" id="box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkInfoBar" id="infobar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="homogeneous">True</property>
- <property name="show_close_button">True</property>
- <property name="revealed">False</property>
- <signal name="response" handler="_on_infobar_response" swapped="no"/>
- <child internal-child="action_area">
- <object class="GtkButtonBox">
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child internal-child="content_area">
- <object class="GtkBox">
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">dialog-information-symbolic</property>
- <property name="icon_size">3</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Click the shield icon to manage trust for each fingerprint.</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">46</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="btbv_link">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Read more on blind trust.</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">18</property>
- <property name="row_spacing">12</property>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="spacing">11</property>
- <child>
- <object class="GtkImage" id="omemo_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="header">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <style>
- <class name="dim-label"/>
- <class name="bold"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkMenuButton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">end</property>
- <property name="valign">center</property>
- <property name="popover">search_popover</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">edit-find-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="vexpand">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <property name="min_content_height">270</property>
- <property name="overlay_scrolling">False</property>
- <child>
- <object class="GtkViewport">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkListBox" id="list">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="selection_mode">none</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- <property name="width">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkMenuButton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">end</property>
- <property name="valign">center</property>
- <property name="direction">up</property>
- <property name="popover">popover</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Own Fingerprint</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="valign">center</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkSwitch">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <signal name="notify::active" handler="_on_show_inactive" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Show inactive</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
-</interface>
diff --git a/omemo/gtk/progress.py b/omemo/gtk/progress.py
deleted file mode 100644
index 3cf5086..0000000
--- a/omemo/gtk/progress.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-
-from gajim.plugins.helpers import get_builder
-
-
-class ProgressWindow:
- def __init__(self, plugin, window, event):
- self._plugin = plugin
- self._event = event
-
- path = self._plugin.local_file_path('gtk/progress.ui')
- self._ui = get_builder(path)
- self._ui.progress_dialog.set_transient_for(window)
- self._ui.progressbar.set_text("")
- self._ui.progress_dialog.show_all()
-
- image_path = self._plugin.local_file_path('omemo.png')
- self._ui.image.set_from_file(image_path)
- self._ui.connect_signals(self)
- self._seen = 0
-
- def set_text(self, text):
- self._ui.label.set_markup('<big>%s</big>' % text)
- return False
-
- def update_progress(self, seen, total):
- self._seen += seen
- pct = (self._seen / float(total)) * 100.0
- self._ui.progressbar.set_fraction(self._seen / float(total))
- self._ui.progressbar.set_text(str(int(pct)) + "%")
- return False
-
- def close_dialog(self, *args):
- self._ui.progress_dialog.destroy()
- return False
-
- def on_destroy(self, *args):
- self._event.set()
diff --git a/omemo/gtk/progress.ui b/omemo/gtk/progress.ui
deleted file mode 100644
index 89eed45..0000000
--- a/omemo/gtk/progress.ui
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
-<interface>
- <requires lib="gtk+" version="3.20"/>
- <object class="GtkDialog" id="progress_dialog">
- <property name="can_focus">True</property>
- <property name="border_width">18</property>
- <property name="title" translatable="yes">Download</property>
- <property name="resizable">False</property>
- <property name="window_position">center-on-parent</property>
- <property name="destroy_with_parent">True</property>
- <property name="icon_name">go-down</property>
- <property name="type_hint">dialog</property>
- <child>
- <placeholder/>
- </child>
- <child internal-child="vbox">
- <object class="GtkBox" id="dialog-vbox">
- <property name="width_request">250</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="dialog-action_area11">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkAlignment" id="alignment3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="top_padding">2</property>
- <property name="bottom_padding">4</property>
- <property name="right_padding">3</property>
- <child>
- <object class="GtkButton" id="close_button">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="close_dialog" swapped="no"/>
- <signal name="destroy" handler="on_destroy" swapped="no"/>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text">OMEMO</property>
- <property name="margin_right">6</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkAlignment" id="alignment1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="top_padding">8</property>
- <property name="bottom_padding">4</property>
- <property name="left_padding">8</property>
- <property name="right_padding">8</property>
- <child>
- <object class="GtkLabel" id="label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_markup">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkAlignment" id="alignment2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="top_padding">4</property>
- <property name="bottom_padding">4</property>
- <property name="left_padding">8</property>
- <property name="right_padding">8</property>
- <child>
- <object class="GtkProgressBar" id="progressbar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="pulse_step">0.10000000149</property>
- <property name="show_text">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/omemo/gtk/style.css b/omemo/gtk/style.css
deleted file mode 100644
index 25cd903..0000000
--- a/omemo/gtk/style.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.omemo-inactive-color { color: @insensitive_fg_color; }
-
-.omemo-qr-not-available {color: red;}
-
-.omemo-mono { font-size: 12px; font-family: monospace; }
-
-.omemo-last-seen { font-size: 11px; }
-
-.omemo-key-dialog scrolledwindow row {
- border-bottom: 1px solid;
- border-color: @unfocused_borders;
- padding: 10px 20px 10px 10px;
-}
-.omemo-key-dialog scrolledwindow row:last-child { border-bottom: 0px}
-.omemo-key-dialog scrolledwindow { border: 1px solid; border-color:@unfocused_borders; }
-.omemo-key-dialog list > row { outline: none; }
-
-.omemo-trust-popover row { padding: 10px 15px 10px 10px; }
diff --git a/omemo/modules/__init__.py b/omemo/modules/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/omemo/modules/__init__.py
+++ /dev/null
diff --git a/omemo/modules/events.py b/omemo/modules/events.py
deleted file mode 100644
index fe394d4..0000000
--- a/omemo/modules/events.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-from __future__ import annotations
-
-from typing import Any
-
-from dataclasses import dataclass
-from dataclasses import field
-
-from gajim.common.events import ApplicationEvent
-
-
-@dataclass
-class OMEMONewFingerprint(ApplicationEvent):
- name: str = field(init=False, default='omemo-new-fingerprint')
- chat_control: Any
diff --git a/omemo/modules/omemo.py b/omemo/modules/omemo.py
deleted file mode 100644
index 548d4af..0000000
--- a/omemo/modules/omemo.py
+++ /dev/null
@@ -1,514 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-# XEP-0384: OMEMO Encryption
-
-import time
-from pathlib import Path
-
-from nbxmpp.namespaces import Namespace
-from nbxmpp.protocol import NodeProcessed
-from nbxmpp.errors import StanzaError
-from nbxmpp.const import PresenceType
-from nbxmpp.const import Affiliation
-from nbxmpp.structs import StanzaHandler
-from nbxmpp.modules.omemo import create_omemo_message
-from nbxmpp.modules.omemo import get_key_transport_message
-from nbxmpp.modules.util import is_error
-
-from gajim.common import app
-from gajim.common import configpaths
-from gajim.common.events import MessageNotSent
-from gajim.common.const import EncryptionData
-from gajim.common.const import Trust as GajimTrust
-from gajim.common.modules.base import BaseModule
-from gajim.common.modules.util import event_node
-from gajim.common.modules.util import as_task
-
-from gajim.plugins.plugins_i18n import _
-
-from omemo.backend.state import OmemoState
-from omemo.backend.state import KeyExchangeMessage
-from omemo.backend.state import SelfMessage
-from omemo.backend.state import MessageNotForDevice
-from omemo.backend.state import DecryptionFailed
-from omemo.backend.state import DuplicateMessage
-from omemo.backend.util import Trust
-from omemo.modules.events import OMEMONewFingerprint
-from omemo.modules.util import prepare_stanza
-
-
-ALLOWED_TAGS = [
- ('request', Namespace.RECEIPTS),
- ('active', Namespace.CHATSTATES),
- ('gone', Namespace.CHATSTATES),
- ('inactive', Namespace.CHATSTATES),
- ('paused', Namespace.CHATSTATES),
- ('composing', Namespace.CHATSTATES),
- ('markable', Namespace.CHATMARKERS),
- ('no-store', Namespace.HINTS),
- ('store', Namespace.HINTS),
- ('no-copy', Namespace.HINTS),
- ('no-permanent-store', Namespace.HINTS),
- ('replace', Namespace.CORRECT),
- ('thread', None),
- ('origin-id', Namespace.SID),
-]
-
-ENCRYPTION_NAME = 'OMEMO'
-
-# Module name
-name = 'OMEMO'
-zeroconf = False
-
-
-class OMEMO(BaseModule):
-
- _nbxmpp_extends = 'OMEMO'
- _nbxmpp_methods = [
- 'set_devicelist',
- 'request_devicelist',
- 'set_bundle',
- 'request_bundle',
- ]
-
- def __init__(self, client):
- BaseModule.__init__(self, client, plugin=True)
-
- self.handlers = [
- StanzaHandler(name='message',
- callback=self._message_received,
- ns=Namespace.OMEMO_TEMP,
- priority=9),
- StanzaHandler(name='presence',
- callback=self._on_muc_user_presence,
- ns=Namespace.MUC_USER,
- priority=48),
- ]
-
- self._register_pubsub_handler(self._devicelist_notification_received)
-
- self.available = True
-
- self._own_jid = self._client.get_own_jid().bare
- self._backend = self._get_backend()
-
- self._omemo_groupchats = set()
- self._muc_temp_store = {}
- self._query_for_bundles = []
- self._device_bundle_querys = []
- self._query_for_devicelists = []
-
- def get_own_jid(self, stripped=False):
- if stripped:
- return self._client.get_own_jid().bare
- return self._client.get_own_jid()
-
- @property
- def backend(self):
- return self._backend
-
- def _get_backend(self):
- data_dir = Path(configpaths.get('MY_DATA'))
- db_path = data_dir / f'omemo_{self._own_jid}.db'
- return OmemoState(self._own_jid, db_path, self._account, self)
-
- def is_omemo_groupchat(self, room_jid):
- return room_jid in self._omemo_groupchats
-
- def on_signed_in(self):
- self._log.info('Announce Support after Sign In')
- self._query_for_bundles = []
- self.set_bundle()
- self.request_devicelist()
-
- def activate(self):
- """ Method called when the Plugin is activated in the PluginManager
- """
- self._client.get_module('Caps').update_caps()
-
- if app.account_is_connected(self._account):
- self._log.info('Announce Support after Plugin Activation')
- self._query_for_bundles = []
- self.set_bundle()
- self.request_devicelist()
-
- def deactivate(self):
- """ Method called when the Plugin is deactivated in the PluginManager
- """
- self._query_for_bundles = []
-
- def encrypt_message(self, conn, event, callback, groupchat):
- if not event.message:
- callback(event)
- return
-
- omemo_message = self.backend.encrypt(event.jid, event.message)
- if omemo_message is None:
- app.ged.raise_event(
- MessageNotSent(client=conn,
- jid=event.jid,
- message=event.message,
- error=_('Encryption error'),
- time=time.time()))
- return
-
- create_omemo_message(event.stanza, omemo_message,
- node_whitelist=ALLOWED_TAGS)
-
- if groupchat:
- self._muc_temp_store[omemo_message.payload] = event.message
- else:
- event.xhtml = None
- event.encrypted = ENCRYPTION_NAME
- event.additional_data['encrypted'] = {
- 'name': ENCRYPTION_NAME,
- 'trust': GajimTrust[Trust.VERIFIED.name]}
-
- self._debug_print_stanza(event.stanza)
- callback(event)
-
- def _send_key_transport_message(self, typ, jid, devices):
- omemo_message = self.backend.encrypt_key_transport(jid, devices)
- if omemo_message is None:
- self._log.warning('Key transport message to %s (%s) failed',
- jid, devices)
- return
-
- transport_message = get_key_transport_message(typ, jid, omemo_message)
- self._log.info('Send key transport message %s (%s)', jid, devices)
- self._client.connection.send(transport_message)
-
- def _message_received(self, _con, stanza, properties):
- if not properties.is_omemo:
- return
-
- if properties.is_carbon_message and properties.carbon.is_sent:
- from_jid = self._own_jid
-
- elif properties.is_mam_message:
- from_jid = self._process_mam_message(properties)
-
- elif properties.from_muc:
- from_jid = self._process_muc_message(properties)
-
- else:
- from_jid = properties.jid.bare
-
- if from_jid is None:
- return
-
- self._log.info('Message received from: %s', from_jid)
-
- try:
- plaintext, fingerprint, trust = self.backend.decrypt_message(
- properties.omemo, from_jid)
- except (KeyExchangeMessage, DuplicateMessage):
- raise NodeProcessed
-
- except SelfMessage:
- if not properties.from_muc:
- raise NodeProcessed
-
- if properties.omemo.payload not in self._muc_temp_store:
- self._log.warning("Can't decrypt own GroupChat Message")
- return
-
- plaintext = self._muc_temp_store[properties.omemo.payload]
- fingerprint = self.backend.own_fingerprint
- trust = Trust.VERIFIED
- del self._muc_temp_store[properties.omemo.payload]
-
- except DecryptionFailed:
- return
-
- except MessageNotForDevice:
- if properties.omemo.payload is None:
- # Key Transport message for another device
- return
-
- plaintext = _('This message was encrypted with OMEMO, '
- 'but not for your device.')
- # Neither trust nor fingerprint can be verified if we didn't
- # successfully decrypt the message
- trust = Trust.UNTRUSTED
- fingerprint = None
-
- prepare_stanza(stanza, plaintext)
- self._debug_print_stanza(stanza)
- properties.encrypted = EncryptionData({'name': ENCRYPTION_NAME,
- 'fingerprint': fingerprint,
- 'trust': GajimTrust[trust.name]})
-
- def _process_muc_message(self, properties):
- resource = properties.jid.resource
- if properties.muc_ofrom is not None:
- # History Message from MUC
- return properties.muc_ofrom.bare
-
- contact = self._client.get_module('Contacts').get_contact(
- properties.jid)
- if contact.real_jid is not None:
- return contact.real_jid.bare
-
- self._log.info('Groupchat: Last resort trying to find SID in DB')
- from_jid = self.backend.storage.getJidFromDevice(properties.omemo.sid)
- if not from_jid:
- self._log.error("Can't decrypt GroupChat Message from %s", resource)
- return
- return from_jid
-
- def _process_mam_message(self, properties):
- self._log.info('Message received, archive: %s', properties.mam.archive)
- if properties.from_muc:
- self._log.info('MUC MAM Message received')
- if properties.muc_user is None or properties.muc_user.jid is None:
- self._log.warning('Received MAM Message which can '
- 'not be mapped to a real jid')
- return
- return properties.muc_user.jid.bare
- return properties.from_.bare
-
- def _on_muc_user_presence(self, _con, _stanza, properties):
- if properties.type == PresenceType.ERROR:
- return
-
- if properties.is_muc_destroyed:
- return
-
- room = properties.jid.bare
-
- if properties.muc_user is None or properties.muc_user.jid is None:
- # No real jid found
- return
-
- jid = properties.muc_user.jid.bare
- if properties.muc_user.affiliation in (Affiliation.OUTCAST,
- Affiliation.NONE):
- self.backend.remove_muc_member(room, jid)
- else:
- self.backend.add_muc_member(room, jid)
-
- if self.is_omemo_groupchat(room):
- if not self.is_contact_in_roster(jid):
- # Query Devicelists from JIDs not in our Roster
- self._log.info('%s not in Roster, query devicelist...', jid)
- self.request_devicelist(jid)
-
- def get_affiliation_list(self, room_jid):
- for affiliation in ('owner', 'admin', 'member'):
- self._nbxmpp('MUC').get_affiliation(
- room_jid,
- affiliation,
- callback=self._on_affiliations_received,
- user_data=room_jid)
-
- def _on_affiliations_received(self, task):
- room_jid = task.get_user_data()
- try:
- result = task.finish()
- except StanzaError as error:
- self._log.info('Affiliation request failed: %s', error)
- return
-
- for user_jid in result.users:
- jid = str(user_jid)
- self.backend.add_muc_member(room_jid, jid)
-
- if not self.is_contact_in_roster(jid):
- # Query Devicelists from JIDs not in our Roster
- self._log.info('%s not in Roster, query devicelist...', jid)
- self.request_devicelist(jid)
-
- def is_contact_in_roster(self, jid):
- if jid == self._own_jid:
- return True
-
- roster_item = self._client.get_module('Roster').get_item(jid)
- if roster_item is None:
- return False
-
- contact = self._client.get_module('Contacts').get_contact(jid)
- return contact.subscription == 'both'
-
- def on_muc_disco_update(self, event):
- self._check_if_omemo_capable(event.jid)
-
- def on_room_joined(self, contact):
- jid = str(contact.jid)
- self._check_if_omemo_capable(jid)
- if self.is_omemo_groupchat(jid):
- self.get_affiliation_list(jid)
-
- def _check_if_omemo_capable(self, jid):
- disco_info = app.storage.cache.get_last_disco_info(jid)
- if disco_info.muc_is_members_only and disco_info.muc_is_nonanonymous:
- self._log.info('OMEMO room discovered: %s', jid)
- self._omemo_groupchats.add(jid)
- else:
- self._log.info('OMEMO room removed due to config change: %s', jid)
- self._omemo_groupchats.discard(jid)
-
- def _check_for_missing_sessions(self, jid):
- devices_without_session = self.backend.devices_without_sessions(jid)
- for device_id in devices_without_session:
- if device_id in self._device_bundle_querys:
- continue
- self._device_bundle_querys.append(device_id)
- self.request_bundle(jid, device_id)
-
- def are_keys_missing(self, contact_jid):
- """ Checks if devicekeys are missing and queries the
- bundles
-
- Parameters
- ----------
- contact_jid : str
- bare jid of the contact
-
- Returns
- -------
- bool
- Returns True if there are no trusted Fingerprints
- """
-
- # Fetch Bundles of own other Devices
- if self._own_jid not in self._query_for_bundles:
-
- devices_without_session = self.backend \
- .devices_without_sessions(self._own_jid)
-
- self._query_for_bundles.append(self._own_jid)
-
- if devices_without_session:
- for device_id in devices_without_session:
- self.request_bundle(self._own_jid, device_id)
-
- # Fetch Bundles of contacts devices
- if contact_jid not in self._query_for_bundles:
-
- devices_without_session = self.backend \
- .devices_without_sessions(contact_jid)
-
- self._query_for_bundles.append(contact_jid)
-
- if devices_without_session:
- for device_id in devices_without_session:
- self.request_bundle(contact_jid, device_id)
-
- if self.backend.has_trusted_keys(contact_jid):
- return False
- return True
-
- def set_bundle(self):
- self._nbxmpp('OMEMO').set_bundle(self.backend.bundle,
- self.backend.own_device)
-
- @as_task
- def request_bundle(self, jid, device_id):
- _task = yield
-
- self._log.info('Fetch device bundle %s %s', device_id, jid)
-
- bundle = yield self._nbxmpp('OMEMO').request_bundle(
- jid,
- device_id)
-
- if is_error(bundle) or bundle is None:
- self._log.info('Bundle request failed: %s %s: %s',
- jid, device_id, bundle)
- return
-
- self.backend.build_session(jid, device_id, bundle)
- self._log.info('Session created for: %s', jid)
- # TODO: In MUC we should send a groupchat message
- self._send_key_transport_message('chat', jid, [device_id])
-
- # Trigger dialog to trust new Fingerprints if
- # the Chat Window is Open
-
- # TODO: This does not work anymore
- # ctrl = app.window.get_control(self._account, jid)
- # if ctrl:
- # app.ged.raise_event(OMEMONewFingerprint(chat_control=ctrl))
-
- def set_devicelist(self, devicelist=None):
- devicelist_ = set([self.backend.own_device])
- if devicelist is not None:
- devicelist_.update(devicelist)
- self._log.info('Publishing own devicelist: %s', devicelist_)
- self._nbxmpp('OMEMO').set_devicelist(devicelist_)
-
- def clear_devicelist(self):
- self.backend.update_devicelist(self._own_jid, [self.backend.own_device])
- self.set_devicelist()
-
- @as_task
- def request_devicelist(self, jid=None):
- _task = yield
-
- if jid is None:
- jid = self._own_jid
-
- if jid in self._query_for_devicelists:
- return
-
- self._query_for_devicelists.append(jid)
-
- devicelist = yield self._nbxmpp('OMEMO').request_devicelist(jid=jid)
- if is_error(devicelist) or devicelist is None:
- self._log.info('Devicelist request failed: %s %s', jid, devicelist)
- devicelist = []
-
- self._process_devicelist_update(jid, devicelist)
-
- @event_node(Namespace.OMEMO_TEMP_DL)
- def _devicelist_notification_received(self, _con, _stanza, properties):
- if properties.pubsub_event.retracted:
- return
-
- devicelist = properties.pubsub_event.data or []
-
- self._process_devicelist_update(str(properties.jid), devicelist)
-
- def _process_devicelist_update(self, jid, devicelist):
- own_devices = jid is None or self._client.get_own_jid().bare_match(jid)
- if own_devices:
- jid = self._own_jid
-
- self._log.info('Received device list for %s: %s', jid, devicelist)
- # Pass a copy, we need the full list for potential set_devicelist()
- self.backend.update_devicelist(jid, list(devicelist))
-
- if jid in self._query_for_bundles:
- self._query_for_bundles.remove(jid)
-
- if own_devices:
- if not self.backend.is_own_device_published:
- # Our own device_id is not in the list, it could be
- # overwritten by some other client
- self.set_devicelist(devicelist)
-
- self._check_for_missing_sessions(jid)
-
- def _debug_print_stanza(self, stanza):
- stanzastr = '\n' + stanza.__str__(fancy=True)
- stanzastr = stanzastr[0:-1]
- self._log.debug(stanzastr)
-
-
-def get_instance(*args, **kwargs):
- return OMEMO(*args, **kwargs), 'OMEMO'
diff --git a/omemo/modules/util.py b/omemo/modules/util.py
deleted file mode 100644
index b93c435..0000000
--- a/omemo/modules/util.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-
-from nbxmpp.namespaces import Namespace
-
-
-def prepare_stanza(stanza, plaintext):
- delete_nodes(stanza, 'encrypted', Namespace.OMEMO_TEMP)
- delete_nodes(stanza, 'body')
- stanza.setBody(plaintext)
-
-
-def delete_nodes(stanza, name, namespace=None):
- nodes = stanza.getTags(name, namespace=namespace)
- for node in nodes:
- stanza.delChild(node)
diff --git a/omemo/omemo.png b/omemo/omemo.png
deleted file mode 100644
index f8d5742..0000000
--- a/omemo/omemo.png
+++ /dev/null
Binary files differ
diff --git a/omemo/omemo16x16.png b/omemo/omemo16x16.png
deleted file mode 100644
index a91e568..0000000
--- a/omemo/omemo16x16.png
+++ /dev/null
Binary files differ
diff --git a/omemo/org.gajim.Gajim.Plugin.omemo.metainfo.xml b/omemo/org.gajim.Gajim.Plugin.omemo.metainfo.xml
deleted file mode 100644
index dcbc494..0000000
--- a/omemo/org.gajim.Gajim.Plugin.omemo.metainfo.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<component type="addon">
- <id>org.gajim.Gajim.Plugin.omemo</id>
- <extends>org.gajim.Gajim</extends>
- <name>OMEMO Plugin</name>
- <summary>XMPP Extension Protocol (XEP) for secure multi-client end-to-end encryption</summary>
- <url type="homepage">https://gajim.org/</url>
- <metadata_license>CC-BY-SA-3.0</metadata_license>
- <project_license>GPL-3.0</project_license>
- <update_contact>gajim-devel_AT_gajim.org</update_contact>
-</component>
-
diff --git a/omemo/plugin-manifest.json b/omemo/plugin-manifest.json
deleted file mode 100644
index 0e5bcb1..0000000
--- a/omemo/plugin-manifest.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "authors": [
- "Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>",
- "Daniel Gultsch <daniel@gultsch.de>",
- "Philipp Hörist <philipp@hoerist.com>"
- ],
- "description": "OMEMO is an XMPP Extension Protocol (XEP) for secure multi-client end-to-end encryption based on Axolotl and PEP. You need to install some dependencies, detailed in the installation instructions for your system in the GitLab wiki.",
- "homepage": "https://dev.gajim.org/gajim/gajim-plugins/wikis/OmemoGajimPlugin",
- "config_dialog": true,
- "name": "OMEMO",
- "platforms": [
- "others",
- "linux",
- "darwin",
- "win32"
- ],
- "requirements": [
- "gajim>=1.5.0"
- ],
- "short_name": "omemo",
- "version": "2.9.0"
-} \ No newline at end of file
diff --git a/omemo/plugin.py b/omemo/plugin.py
deleted file mode 100644
index dd5d5cc..0000000
--- a/omemo/plugin.py
+++ /dev/null
@@ -1,342 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
-# Copyright (C) 2015 Daniel Gultsch <daniel@cgultsch.de>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin 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; version 3 only.
-#
-# OMEMO Gajim Plugin 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 OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-import logging
-import binascii
-import threading
-from enum import IntEnum, unique
-from pathlib import Path
-from functools import partial
-
-from gi.repository import GLib
-from gi.repository import Gtk
-from gi.repository import Gdk
-
-from nbxmpp.namespaces import Namespace
-
-from gajim.common import app, ged
-from gajim.common.modules.contacts import GroupchatContact
-
-from gajim.gtk.dialogs import ErrorDialog
-
-from gajim.plugins import GajimPlugin
-from gajim.plugins.plugins_i18n import _
-
-AXOLOTL_MISSING = 'You are missing Python3-Axolotl or use an outdated version'
-PROTOBUF_MISSING = "OMEMO can't import Google Protobuf, you can find help in " \
- "the GitLab Wiki"
-ERROR_MSG = ''
-
-
-log = logging.getLogger('gajim.p.omemo')
-if log.getEffectiveLevel() == logging.DEBUG:
- log_axolotl = logging.getLogger('axolotl')
- log_axolotl.setLevel(logging.DEBUG)
- log_axolotl.addHandler(logging.StreamHandler())
- log_axolotl.propagate = False
-
-try:
- import google.protobuf
-except Exception as error:
- log.error(error)
- ERROR_MSG = PROTOBUF_MISSING
-
-try:
- import axolotl
-except Exception as error:
- log.error(error)
- ERROR_MSG = AXOLOTL_MISSING
-
-if not ERROR_MSG:
- try:
- from omemo.modules import omemo
- from omemo.gtk.key import KeyDialog
- from omemo.gtk.config import OMEMOConfigDialog
- from omemo.backend.aes import aes_encrypt_file
- except Exception as error:
- log.error(error)
- ERROR_MSG = 'Error: %s' % error
-
-
-@unique
-class UserMessages(IntEnum):
- QUERY_DEVICES = 0
- NO_FINGERPRINTS = 1
- UNDECIDED_FINGERPRINTS = 2
-
-
-class OmemoPlugin(GajimPlugin):
- def init(self):
- # pylint: disable=attribute-defined-outside-init
- if ERROR_MSG:
- self.activatable = False
- self.available_text = ERROR_MSG
- self.config_dialog = None
- return
- self.encryption_name = 'OMEMO'
- self.allow_groupchat = True
- self.events_handlers = {
- 'omemo-new-fingerprint': (ged.PRECORE, self._on_new_fingerprints),
- 'signed-in': (ged.PRECORE, self._on_signed_in),
- 'muc-disco-update': (ged.GUI1, self._on_muc_disco_update),
- 'muc-added': (ged.GUI1, self._on_muc_added),
- }
- self.modules = [omemo]
-
- self.config_dialog = partial(OMEMOConfigDialog, self)
- self.gui_extension_points = {
- 'encrypt' + self.encryption_name: (self._encrypt_message, None),
- 'gc_encrypt' + self.encryption_name: (
- self._muc_encrypt_message, None),
- 'send_message' + self.encryption_name: (
- self._before_sendmessage, None),
- 'encryption_dialog' + self.encryption_name: (
- self._on_encryption_button_clicked, None),
- 'encryption_state' + self.encryption_name: (
- self._encryption_state, None),
- 'update_caps': (self._update_caps, None),
- }
-
- self.disabled_accounts = []
- self._windows = {}
-
- self.config_default_values = {
- 'DISABLED_ACCOUNTS': ([], ''),
- 'BLIND_TRUST': (True, ''),
- 'SHOW_HELP_FINGERPRINTS': (True, ''),
- }
-
- for account in self.config['DISABLED_ACCOUNTS']:
- self.disabled_accounts.append(account)
-
- self._load_css()
-
- def _is_enabled_account(self, account):
- if account in self.disabled_accounts:
- return False
- if account == 'Local':
- return False
- return True
-
- @staticmethod
- def get_omemo(account):
- return app.get_client(account).get_module('OMEMO')
-
- @staticmethod
- def _load_css():
- path = Path(__file__).parent / 'gtk' / 'style.css'
- try:
- with path.open("r") as file:
- css = file.read()
- except Exception as exc:
- log.error('Error loading css: %s', exc)
- return
-
- try:
- provider = Gtk.CssProvider()
- provider.load_from_data(bytes(css.encode('utf-8')))
- Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
- provider, 610)
- except Exception:
- log.exception('Error loading application css')
-
- def activate(self):
- """
- Method called when the Plugin is activated in the PluginManager
- """
- for account in app.settings.get_active_accounts():
- if not self._is_enabled_account(account):
- continue
- self.get_omemo(account).activate()
-
- def deactivate(self):
- """
- Method called when the Plugin is deactivated in the PluginManager
- """
- for account in app.settings.get_active_accounts():
- if not self._is_enabled_account(account):
- continue
- self.get_omemo(account).deactivate()
-
- def _on_signed_in(self, event):
- if not self._is_enabled_account(event.account):
- return
- self.get_omemo(event.account).on_signed_in()
-
- def _on_muc_disco_update(self, event):
- if not self._is_enabled_account(event.account):
- return
- self.get_omemo(event.account).on_muc_disco_update(event)
-
- def _on_room_joined(self, contact, _signal_name: str):
- if not self._is_enabled_account(contact.account):
- return
- self.get_omemo(contact.account).on_room_joined(contact)
-
- def _update_caps(self, account, features):
- if not self._is_enabled_account(account):
- return
- features.append('%s+notify' % Namespace.OMEMO_TEMP_DL)
-
- @staticmethod
- def activate_encryption(chat_control):
- return True
-
- def _muc_encrypt_message(self, conn, obj, callback):
- account = conn.name
- if not self._is_enabled_account(account):
- return
- self.get_omemo(account).encrypt_message(conn, obj, callback, True)
-
- def _encrypt_message(self, conn, obj, callback):
- account = conn.name
- if not self._is_enabled_account(account):
- return
- self.get_omemo(account).encrypt_message(conn, obj, callback, False)
-
- def encrypt_file(self, file, _account, callback):
- thread = threading.Thread(target=self._encrypt_file_thread,
- args=(file, callback))
- thread.daemon = True
- thread.start()
-
- @staticmethod
- def _encrypt_file_thread(file, callback, *args, **kwargs):
- result = aes_encrypt_file(file.get_data())
- file.size = len(result.payload)
- fragment = binascii.hexlify(result.iv + result.key).decode()
- file.set_uri_transform_func(
- lambda uri: 'aesgcm%s#%s' % (uri[5:], fragment))
- file.set_encrypted_data(result.payload)
- GLib.idle_add(callback, file)
-
- @staticmethod
- def _encryption_state(_chat_control, state):
- state['visible'] = True
- state['authenticated'] = True
-
- def _on_encryption_button_clicked(self, chat_control):
- self._show_fingerprint_window(chat_control)
-
- def _before_sendmessage(self, chat_control):
- account = chat_control.account
- if not self._is_enabled_account(account):
- return
- contact = chat_control.contact
- omemo = self.get_omemo(account)
- self.new_fingerprints_available(chat_control)
- if chat_control.is_groupchat:
- room = chat_control.room_jid
- if not omemo.is_omemo_groupchat(room):
- ErrorDialog(
- _('Bad Configuration'),
- _('To use OMEMO in a Groupchat, the Groupchat should be'
- ' non-anonymous and members-only.'))
- chat_control.sendmessage = False
- return
-
- missing = True
- for jid in omemo.backend.get_muc_members(room):
- if not omemo.are_keys_missing(jid):
- missing = False
- if missing:
- log.info('%s => No Trusted Fingerprints for %s',
- account, room)
- self.print_message(chat_control, UserMessages.NO_FINGERPRINTS)
- chat_control.sendmessage = False
- else:
- # check if we have devices for the contact
- if not omemo.backend.get_devices(contact.jid, without_self=True):
- omemo.request_devicelist(contact.jid)
- self.print_message(chat_control, UserMessages.QUERY_DEVICES)
- chat_control.sendmessage = False
- return
- # check if bundles are missing for some devices
- if omemo.backend.storage.hasUndecidedFingerprints(contact.jid):
- log.info('%s => Undecided Fingerprints for %s',
- account, contact.jid)
- self.print_message(chat_control, UserMessages.UNDECIDED_FINGERPRINTS)
- chat_control.sendmessage = False
- else:
- log.debug('%s => Sending Message to %s',
- account, contact.jid)
-
- def _on_new_fingerprints(self, event):
- self.new_fingerprints_available(event.chat_control)
-
- def new_fingerprints_available(self, chat_control):
- jid = chat_control.contact.jid
- account = chat_control.account
- omemo = self.get_omemo(account)
- if chat_control.is_groupchat:
- for jid_ in omemo.backend.get_muc_members(chat_control.room_jid,
- without_self=False):
- fingerprints = omemo.backend.storage.getNewFingerprints(jid_)
- if fingerprints:
- self._show_fingerprint_window(
- chat_control, fingerprints)
- break
- else:
- fingerprints = omemo.backend.storage.getNewFingerprints(jid)
- if fingerprints:
- self._show_fingerprint_window(
- chat_control, fingerprints)
-
- def _show_fingerprint_window(self, chat_control, fingerprints=None):
- contact = chat_control.contact
- account = chat_control.account
- omemo = self.get_omemo(account)
-
- if 'dialog' not in self._windows:
- self._windows['dialog'] = \
- KeyDialog(self, contact, app.window,
- self._windows, groupchat=chat_control.is_groupchat)
- if fingerprints:
- log.debug('%s => Showing Fingerprint Prompt for %s',
- account, contact.jid)
- omemo.backend.storage.setShownFingerprints(fingerprints)
- else:
- self._windows['dialog'].present()
- self._windows['dialog'].update()
- if fingerprints:
- omemo.backend.storage.setShownFingerprints(fingerprints)
-
- @staticmethod
- def print_message(chat_control, kind):
- msg = None
- if kind == UserMessages.QUERY_DEVICES:
- msg = _('No devices found. Query in progress...')
- elif kind == UserMessages.NO_FINGERPRINTS:
- msg = _('To send an encrypted message, you have to '
- 'first trust the fingerprint of your contact!')
- elif kind == UserMessages.UNDECIDED_FINGERPRINTS:
- msg = _('You have undecided fingerprints')
- if msg is None:
- return
- chat_control.add_info_message(msg)
-
- def _on_muc_added(self, event):
- client = app.get_client(event.account)
- contact = client.get_module('Contacts').get_contact(event.jid)
- if not isinstance(contact, GroupchatContact):
- log.warning('%s is not a groupchat contact', contact)
- return
-
- # Event is triggert on every join, avoid multiple connects
- contact.disconnect_all_from_obj(self)
- contact.connect('room-joined', self._on_room_joined)