diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/Sailfish/ownNotes/datas/changelog.json b/Sailfish/ownNotes/datas/changelog.json
index a94fe30..40b49db 100755
--- a/Sailfish/ownNotes/datas/changelog.json
+++ b/Sailfish/ownNotes/datas/changelog.json
@@ -146,5 +146,101 @@
"text": "Fix pulley menu always showing Syncing label",
"type": "bug"
}
+ ],
+ "1.8.1": [
+ {
+ "text": "Rewrite in python3",
+ "type": "feature"
+ },
+ {
+ "text": "Add missing file rfc8222 lib",
+ "type": "bug"
+ },
+ {
+ "text": "Improving release scrypt",
+ "type": "bug"
+ }
+ ],
+ "1.8.2": [
+ {
+ "text": "Use encryption only when id of machine is available",
+ "type": "feature"
+ },
+ {
+ "text": "Add more debug trace",
+ "type": "feature"
+ },
+ {
+ "text": "Add optionnal log for sync",
+ "type": "feature"
+ },
+ {
+ "text": "Store password encrypted",
+ "type": "feature"
+ },
+ {
+ "text": "Add datetime of last sync in cover",
+ "type": "feature"
+ },
+ {
+ "text": "Some PEP8 cleaning",
+ "type": "feature"
+ },
+ {
+ "text": "Raise a error in case of IO Error when reading a note",
+ "type": "feature"
+ },
+ {
+ "text": "Remove uselessC",
+ "type": "bug"
+ },
+ {
+ "text": "Fix timedelta bug",
+ "type": "bug"
+ },
+ {
+ "text": "Fix update of note model when note deleted",
+ "type": "bug"
+ },
+ {
+ "text": "Fix bug in sync",
+ "type": "bug"
+ },
+ {
+ "text": "Fix logger and add some informations",
+ "type": "bug"
+ },
+ {
+ "text": "Fix password setting one more time ... do not code in train",
+ "type": "bug"
+ },
+ {
+ "text": "Remove debug trace",
+ "type": "bug"
+ },
+ {
+ "text": "Rename ownnote sync log",
+ "type": "bug"
+ },
+ {
+ "text": "Fix saving of encrypted password",
+ "type": "bug"
+ },
+ {
+ "text": "Fix saving of encrypted password",
+ "type": "bug"
+ },
+ {
+ "text": "Fix use of lock tocken in sync",
+ "type": "bug"
+ },
+ {
+ "text": "Fix for utf-16 note",
+ "type": "bug"
+ },
+ {
+ "text": "Remove some utf-8 debug printing that could raise exception if stdout isnt in utf-8",
+ "type": "bug"
+ }
]
}
\ No newline at end of file
diff --git a/Sailfish/ownNotes/i18n/ownNotes_en.ts b/Sailfish/ownNotes/i18n/ownNotes_en.ts
index 162eafc..6a14486 100755
--- a/Sailfish/ownNotes/i18n/ownNotes_en.ts
+++ b/Sailfish/ownNotes/i18n/ownNotes_en.ts
@@ -49,7 +49,7 @@
- Search
+ Search Titles
@@ -65,7 +65,7 @@
- Sync
+ Synchronise
diff --git a/Sailfish/ownNotes/icons/about.png b/Sailfish/ownNotes/icons/about.png
new file mode 100644
index 0000000..142ac65
Binary files /dev/null and b/Sailfish/ownNotes/icons/about.png differ
diff --git a/Sailfish/ownNotes/icons/about.svg b/Sailfish/ownNotes/icons/about.svg
new file mode 100644
index 0000000..ae472c8
--- /dev/null
+++ b/Sailfish/ownNotes/icons/about.svg
@@ -0,0 +1,113 @@
+
+
+
+
diff --git a/Sailfish/ownNotes/icons/coverlarge.png b/Sailfish/ownNotes/icons/coverlarge.png
new file mode 100644
index 0000000..757d63a
Binary files /dev/null and b/Sailfish/ownNotes/icons/coverlarge.png differ
diff --git a/Sailfish/ownNotes/icons/coverlarge.svg b/Sailfish/ownNotes/icons/coverlarge.svg
new file mode 100644
index 0000000..eb131e4
--- /dev/null
+++ b/Sailfish/ownNotes/icons/coverlarge.svg
@@ -0,0 +1,129 @@
+
+
+
+
diff --git a/Sailfish/ownNotes/icons/coversmall.png b/Sailfish/ownNotes/icons/coversmall.png
new file mode 100644
index 0000000..8e166ec
Binary files /dev/null and b/Sailfish/ownNotes/icons/coversmall.png differ
diff --git a/Sailfish/ownNotes/icons/coversmall.svg b/Sailfish/ownNotes/icons/coversmall.svg
new file mode 100644
index 0000000..a903511
--- /dev/null
+++ b/Sailfish/ownNotes/icons/coversmall.svg
@@ -0,0 +1,129 @@
+
+
+
+
diff --git a/Sailfish/ownNotes/icons/ownnotes.png b/Sailfish/ownNotes/icons/ownnotes.png
index 6c6b669..d3307f6 100755
Binary files a/Sailfish/ownNotes/icons/ownnotes.png and b/Sailfish/ownNotes/icons/ownnotes.png differ
diff --git a/Sailfish/ownNotes/icons/ownnotes.svg b/Sailfish/ownNotes/icons/ownnotes.svg
old mode 100755
new mode 100644
index 8824862..8193ee0
--- a/Sailfish/ownNotes/icons/ownnotes.svg
+++ b/Sailfish/ownNotes/icons/ownnotes.svg
@@ -8,23 +8,17 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="24.271111mm"
+ height="24.271111mm"
+ viewBox="0 0 85.999998 85.999998"
+ id="svg5620"
version="1.1"
- width="86"
- height="86"
- id="svg2">
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="ownnotes.svg">
-
-
-
-
+ id="defs5622">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ id="linearGradient3789">
+
+
+
-
-
-
-
+ gradientTransform="matrix(-0.95555556,0,0,0.95555556,43.741381,-257.7973)"
+ spreadMethod="reflect"
+ x1="45"
+ y1="10"
+ x2="45"
+ y2="80" />
+
+ id="metadata5625">
@@ -191,689 +96,49 @@
-
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-311.39955,-480.18673)">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- image/svg+xml
-
- Sticky note and pencil
- 14 Jun 08
-
-
- Jeremy Bennett
-
-
-
-
- Public Domain
-
-
-
-
- Jeremy Bennett
-
-
-
-
- Richard Tallent and Barret Ruttan from the Open Clip Art library
-
-
- A pencil writing on a sticky note. Created for use as a "note" admonition with Docbook and FOP
-
-
- color
- icon
- office
- computer
- docbook
- admonition
-
-
- en-GB
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ transform="translate(353.65817,737.98403)"
+ style="display:inline"
+ id="g5584">
+
+
+
+
+
+
diff --git a/Sailfish/ownNotes/ownNotes.png b/Sailfish/ownNotes/ownNotes.png
index 6c6b669..d3307f6 100755
Binary files a/Sailfish/ownNotes/ownNotes.png and b/Sailfish/ownNotes/ownNotes.png differ
diff --git a/Sailfish/ownNotes/ownNotes.pro b/Sailfish/ownNotes/ownNotes.pro
index 22c29a8..b4785b5 100755
--- a/Sailfish/ownNotes/ownNotes.pro
+++ b/Sailfish/ownNotes/ownNotes.pro
@@ -25,8 +25,7 @@ DEFINES *= 'VERSION=\'\"$${VERSION}\"\''
SOURCES += src/ownNotes.cpp \
src/qpython.cpp \
src/documenthandler.cpp \
- src/highlighter.cpp \
- src/fileio.cpp
+ src/highlighter.cpp
OTHER_FILES += qml/ownNotes.qml \
qml/cover/CoverPage.qml \
@@ -45,8 +44,7 @@ OTHER_FILES += qml/ownNotes.qml \
HEADERS += \
src/qpython.h \
src/documenthandler.h \
- src/highlighter.h \
- src/fileio.h
+ src/highlighter.h
python_files.files = python
python_files.path = /usr/share/$$TARGET
diff --git a/Sailfish/ownNotes/python/davlib.py b/Sailfish/ownNotes/python/davlib.py
deleted file mode 100755
index da1da76..0000000
--- a/Sailfish/ownNotes/python/davlib.py
+++ /dev/null
@@ -1,407 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# pylint: disable-msg=W0402,W0231,W0141,R0903,C0321,W0701,R0904,C0103
-# pylint: disable-msg=W0201,W0102,R0913,W0622,E1101,C0111,C0121,R0901
-# DAV client library
-#
-# Copyright (C) 1998-2000 Guido van Rossum. All Rights Reserved.
-# Written by Greg Stein. Given to Guido. Licensed using the Python license.
-#
-# This module is maintained by Greg and is available at:
-# http://www.lyra.org/greg/python/davlib.py
-#
-# Since this isn't in the Python distribution yet, we'll use the CVS ID
-# for tracking:
-# $Id: davlib.py 3182 2008-02-22 15:57:55 +0000 (Fr, 22 Feb 2008) schlauch $
-#
-
-import http.client
-import urllib.request, urllib.parse, urllib.error
-import string
-import types
-import mimetypes
-import qp_xml
-import socket
-try:
- import ssl
-except:
- print('SSL Module not available')
-import sys
-
-INFINITY = 'infinity'
-XML_DOC_HEADER = ''
-XML_CONTENT_TYPE = 'text/xml; charset="utf-8"'
-
-# block size for copying files up to the server
-BLOCKSIZE = 16384
-
-'''
-class HTTPSConnectionV3(http.client.HTTPSConnection):
-
- def __init__(self, *args, **kwargs):
- http.client.HTTPSConnection.__init__(self, *args, **kwargs)
- self.timeout = 10.0
-
- def connect(self):
- sock = socket.create_connection((self.host, self.port), self.timeout)
- if sys.version_info < (2, 6, 7):
- if hasattr(self, '_tunnel_host'):
- if self._tunnel_host is not None:
- self.sock = sock
- self._tunnel()
- else:
- if self._tunnel_host:
- self.sock = sock
- self._tunnel()
- try:
- self.sock = ssl.wrap_socket(sock,
- self.key_file,
- self.cert_file,
- ssl_version=ssl.PROTOCOL_SSLv3)
- except ssl.SSLError:
- self.sock = ssl.wrap_socket(sock,
- self.key_file,
- self.cert_file,
- ssl_version=ssl.PROTOCOL_SSLv23)
-
-
-class HTTPProtocolChooser(HTTPSConnectionV3):
-
- def __init__(self, *args, **kw):
- self.protocol = kw.pop('protocol')
- if self.protocol == "https":
- self.default_port = 443
- else:
- self.default_port = 80
-
- http.client.HTTPSConnection.__init__(*(self,) + args, **kw)
-
- def connect(self):
- if self.protocol == "https":
- http.client.HTTPSConnection.connect(self)
- else:
- http.client.HTTPConnection.connect(self)
-'''
-
-
-class HTTPProtocolChooser(http.client.HTTPSConnection):
- def __init__(self, *args, **kw):
- self.protocol = kw.pop('protocol')
- if self.protocol == "https":
- self.default_port = 443
- else:
- self.default_port = 80
-
- http.client.HTTPSConnection.__init__(self,*args, **kw)
-
- def connect(self):
- if self.protocol == "https":
- http.client.HTTPSConnection.connect(self)
- else:
- http.client.HTTPConnection.connect(self)
-
-
-class HTTPConnectionAuth(HTTPProtocolChooser):
- def __init__(self, *args, **kw):
- HTTPProtocolChooser.__init__(self, *args, **kw)
-
- self.__username = None
- self.__password = None
- self.__nonce = None
- self.__opaque = None
-
- def setauth(self, username, password):
- self.__username = username
- self.__password = password
-
-
-def _parse_status(elem):
- text = elem.textof()
- idx1 = string.find(text, ' ')
- idx2 = string.find(text, ' ', idx1 + 1)
- return int(text[idx1:idx2]), text[idx2 + 1:]
-
-
-class _blank:
-
- def __init__(self, **kw):
- self.__dict__.update(kw)
-
-
-class _propstat(_blank):
- pass
-
-
-class _response(_blank):
- pass
-
-
-class _multistatus(_blank):
- pass
-
-
-def _extract_propstat(elem):
- ps = _propstat(prop={}, status=None, responsedescription=None)
- for child in elem.children:
- if child.ns != 'DAV:':
- continue
- if child.name == 'prop':
- for prop in child.children:
- ps.prop[(prop.ns, prop.name)] = prop
- elif child.name == 'status':
- ps.status = _parse_status(child)
- elif child.name == 'responsedescription':
- ps.responsedescription = child.textof()
- # unknown element name
-
- return ps
-
-
-def _extract_response(elem):
- resp = _response(
- href=[], status=None, responsedescription=None, propstat=[])
- for child in elem.children:
- if child.ns != 'DAV:':
- continue
- if child.name == 'href':
- resp.href.append(child.textof())
- elif child.name == 'status':
- resp.status = _parse_status(child)
- elif child.name == 'responsedescription':
- resp.responsedescription = child.textof()
- elif child.name == 'propstat':
- resp.propstat.append(_extract_propstat(child))
- # unknown child element
-
- return resp
-
-
-def _extract_msr(root):
- if root.ns != 'DAV:' or root.name != 'multistatus':
- raise 'invalid response: expected'
-
- msr = _multistatus(responses=[], responsedescription=None)
-
- for child in root.children:
- if child.ns != 'DAV:':
- continue
- if child.name == 'responsedescription':
- msr.responsedescription = child.textof()
- elif child.name == 'response':
- msr.responses.append(_extract_response(child))
- # unknown child element
-
- return msr
-
-
-def _extract_locktoken(root):
- if root.ns != 'DAV:' or root.name != 'prop':
- raise 'invalid response: expected'
- elem = root.find('lockdiscovery', 'DAV:')
- if not elem:
- raise 'invalid response: expected'
- elem = elem.find('activelock', 'DAV:')
- if not elem:
- raise 'invalid response: expected'
- elem = elem.find('locktoken', 'DAV:')
- if not elem:
- raise 'invalid response: expected'
- elem = elem.find('href', 'DAV:')
- if not elem:
- raise 'invalid response: expected'
- return elem.textof()
-
-
-class DAVResponse(http.client.HTTPResponse):
-
- def parse_multistatus(self):
- self.root = qp_xml.Parser().parse(self)
- self.msr = _extract_msr(self.root)
-
- def parse_lock_response(self):
- self.root = qp_xml.Parser().parse(self)
- self.locktoken = _extract_locktoken(self.root)
-
-
-class DAV(HTTPConnectionAuth):
-
- response_class = DAVResponse
-
- def get(self, url, extra_hdrs={}):
- return self._request('GET', url, extra_hdrs=extra_hdrs)
-
- def head(self, url, extra_hdrs={}):
- return self._request('HEAD', url, extra_hdrs=extra_hdrs)
-
- def post(self, url, data={}, body=None, extra_hdrs={}):
- headers = extra_hdrs.copy()
-
- assert body or data, "body or data must be supplied"
- assert not (body and data), "cannot supply both body and data"
- if data:
- body = ''
- for key, value in list(data.items()):
- if isinstance(value, list):
- for item in value:
- body = body + '&' + key + '=' + urllib.parse.quote(str(item))
- else:
- body = body + '&' + key + '=' + urllib.parse.quote(str(value))
- body = body[1:]
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
-
- return self._request('POST', url, body, headers)
-
- def options(self, url='*', extra_hdrs={}):
- return self._request('OPTIONS', url, extra_hdrs=extra_hdrs)
-
- def trace(self, url, extra_hdrs={}):
- return self._request('TRACE', url, extra_hdrs=extra_hdrs)
-
- def put(self, url, contents,
- content_type=None, content_enc=None, extra_hdrs={}):
-
- if not content_type:
- content_type, content_enc = mimetypes.guess_type(url)
-
- headers = extra_hdrs.copy()
- if content_type:
- headers['Content-Type'] = content_type
- if content_enc:
- headers['Content-Encoding'] = content_enc
- return self._request('PUT', url, contents, headers)
-
- def delete(self, url, extra_hdrs={}):
- return self._request('DELETE', url, extra_hdrs=extra_hdrs)
-
- def propfind(self, url, body=None, depth=None, extra_hdrs={}):
- headers = extra_hdrs.copy()
- headers['Content-Type'] = XML_CONTENT_TYPE
- if depth is not None:
- headers['Depth'] = str(depth)
- return self._request('PROPFIND', url, body, headers)
-
- def proppatch(self, url, body, extra_hdrs={}):
- headers = extra_hdrs.copy()
- headers['Content-Type'] = XML_CONTENT_TYPE
- return self._request('PROPPATCH', url, body, headers)
-
- def mkcol(self, url, extra_hdrs={}):
- return self._request('MKCOL', url, extra_hdrs=extra_hdrs)
-
- def move(self, src, dst, extra_hdrs={}):
- headers = extra_hdrs.copy()
- headers['Destination'] = dst
- return self._request('MOVE', src, extra_hdrs=headers)
-
- def copy(self, src, dst, depth=None, extra_hdrs={}):
- headers = extra_hdrs.copy()
- headers['Destination'] = dst
- if depth is not None:
- headers['Depth'] = str(depth)
- return self._request('COPY', src, extra_hdrs=headers)
-
- def lock(self, url, owner='', timeout=None, depth=None,
- scope='exclusive', type='write', extra_hdrs={}):
- headers = extra_hdrs.copy()
- headers['Content-Type'] = XML_CONTENT_TYPE
- if depth is not None:
- headers['Depth'] = str(depth)
- if timeout is not None:
- headers['Timeout'] = timeout
- body = XML_DOC_HEADER + \
- '' + \
- '' % scope + \
- '' % type + \
- '' + owner + '' + \
- ''
- return self._request('LOCK', url, body, extra_hdrs=headers)
-
- def unlock(self, url, locktoken, extra_hdrs={}):
- headers = extra_hdrs.copy()
- if locktoken[0] != '<':
- locktoken = '<' + locktoken + '>'
- headers['Lock-Token'] = locktoken
- return self._request('UNLOCK', url, extra_hdrs=headers)
-
- def _request(self, method, url, body=None, extra_hdrs={}):
- "Internal method for sending a request."
-
- self.request(method, url, body, extra_hdrs)
- return self.getresponse()
-
- #
- # Higher-level methods for typical client use
- #
- def allprops(self, url, depth=None):
- body = XML_DOC_HEADER + \
- ''
- return self.propfind(url, body, depth=depth)
-
- def propnames(self, url, depth=None):
- body = XML_DOC_HEADER + \
- ''
- return self.propfind(url, body, depth)
-
- def getprops(self, url, *names, **kw):
- assert names, 'at least one property name must be provided'
- if 'ns' in kw:
- xmlns = ' xmlns:NS="' + kw['ns'] + '"'
- ns = 'NS:'
- del kw['ns']
- else:
- xmlns = ns = ''
- if 'depth' in kw:
- depth = kw['depth']
- del kw['depth']
- else:
- depth = 0
- assert not kw, 'unknown arguments'
- body = XML_DOC_HEADER + \
- '<' + ns + \
- string.joinfields(names, '/><' + ns) + \
- '/>'
- return self.propfind(url, body, depth)
-
- def delprops(self, url, *names, **kw):
- assert names, 'at least one property name must be provided'
- if 'ns' in kw:
- xmlns = ' xmlns:NS="' + kw['ns'] + '"'
- ns = 'NS:'
- del kw['ns']
- else:
- xmlns = ns = ''
- assert not kw, 'unknown arguments'
- body = XML_DOC_HEADER + \
- '<' + ns + \
- string.joinfields(names, '/><' + ns) + \
- '/>'
- return self.proppatch(url, body)
-
- def setprops(self, url, *xmlprops, **props):
- assert xmlprops or props, 'at least one property must be provided'
- xmlprops = list(xmlprops)
- if 'ns' in props:
- xmlns = ' xmlns:NS="' + props['ns'] + '"'
- ns = 'NS:'
- del props['ns']
- else:
- xmlns = ns = ''
- for key, value in list(props.items()):
- if value:
- xmlprops.append('<%s%s>%s%s%s>' % (ns, key, value, ns, key))
- else:
- xmlprops.append('<%s%s/>' % (ns, key))
- elems = string.joinfields(xmlprops, '')
- body = XML_DOC_HEADER + \
- '' + \
- elems + \
- ''
- return self.proppatch(url, body)
-
- def get_lock(self, url, owner='', timeout=None, depth=None):
- response = self.lock(url, owner, timeout, depth)
- response.parse_lock_response()
- return response.locktoken
diff --git a/Sailfish/ownNotes/python/fakelock.py b/Sailfish/ownNotes/python/fakelock.py
new file mode 100644
index 0000000..8e50d90
--- /dev/null
+++ b/Sailfish/ownNotes/python/fakelock.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+"""Fake a WebDAV LOCK request.
+
+OwnCloud doesn't support WebDAV LOCKs, which return a 501 error. This class
+fakes the interface so that the locking code can be kept in the sync
+code but not have it actually do anything.
+
+It doesn't provide any locking functionality, it just offers an interface
+to allow 'with' blocks that have no effect.
+
+See ownCloud issue #17732 for details about the lack of WebDAV LOCKs:
+https://github.com/owncloud/core/issues/17732
+"""
+
+import contextlib
+
+@contextlib.contextmanager
+
+def fakelock():
+ """
+ Empty context manager that does nothing. Courtesey of @brett_lempereurs
+ pithiness :)
+ https://gist.github.com/brett-lempereur/31c67d8d3b251bd5175e104e656d781c
+ https://twitter.com/brettlempereur/status/773566420709994496
+ """
+ yield
diff --git a/Sailfish/ownNotes/python/logger.py b/Sailfish/ownNotes/python/logger.py
index 2115b6d..6cac4ed 100755
--- a/Sailfish/ownNotes/python/logger.py
+++ b/Sailfish/ownNotes/python/logger.py
@@ -20,29 +20,31 @@
"""
import logging
-import sys
-
-
-_defaultLoggerName = "ownNotes"
-_fileLogFormat = "%(asctime)s: %(levelname)s: %(message)s"
-
-
-def getDefaultLogger(handler=None):
- """
- Returns a configured logger object.
-
- @return: Logger instance.
- @rtype: C{logging.Logger}
- """
-
- myLogger = logging.getLogger(_defaultLoggerName)
- if len(myLogger.handlers) == 0:
- myLogger.level = logging.DEBUG
- formatter = logging.Formatter(_fileLogFormat)
- if handler is None:
- stdoutHandler = logging.StreamHandler(sys.stdout)
- stdoutHandler.setFormatter(formatter)
- myLogger.addHandler(stdoutHandler)
- else:
- myLogger.addHandler(handler)
- return myLogger
+from logging.handlers import RotatingFileHandler
+import os
+
+
+class Logger():
+
+ def __init__(self, debug=False):
+ self.logger = logging.getLogger('ownNotes')
+ formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
+
+ if debug:
+ # File Log
+ file_hdlr = RotatingFileHandler(os.path.join(
+ os.path.expanduser('~/.ownnotes/'),
+ 'OwnNotes Log.log'),
+ 100000, 1)
+ file_hdlr.setFormatter(formatter)
+ file_hdlr.setLevel(logging.DEBUG)
+ self.logger.addHandler(file_hdlr)
+
+ # Steam Log
+ steam_hdlr = logging.StreamHandler()
+ steam_hdlr.setFormatter(formatter)
+ steam_hdlr.setLevel(logging.DEBUG)
+ self.logger.addHandler(steam_hdlr)
+
+ if debug:
+ self.logger.setLevel(logging.DEBUG)
diff --git a/Sailfish/ownNotes/python/ownnotes.py b/Sailfish/ownNotes/python/ownnotes.py
index 2240dc8..fd00897 100755
--- a/Sailfish/ownNotes/python/ownnotes.py
+++ b/Sailfish/ownNotes/python/ownnotes.py
@@ -20,11 +20,13 @@
"""
import os
+import os.path
import time
import re
import html.entities
from settings import Settings
from sync import Sync
+import logger
INVALID_FILENAME_CHARS = '\/:*?"<>|'
STRIPTAGS = re.compile(r'<[^>]+>')
@@ -140,15 +142,15 @@ def fixup(m):
return chr(int(text[3:-1], 16))
else:
return chr(int(text[2:-1]))
- except ValueError as e:
- print(e)
+ except ValueError as err:
+ logger.Logger().logger.error(str(err))
else:
# named entity
try:
text = chr(
html.entities.name2codepoint[text[1:-1]])
- except KeyError as e:
- print(e)
+ except KeyError as err:
+ logger.Logger().logger.error(str(err))
return text # leave as is
return re.sub("?\w+;", fixup, text)
@@ -198,69 +200,91 @@ def saveNote(filepath, data, colorized=True):
try:
os.rename(filepath, new_path)
except OSError:
- print('Old didn\'t exists')
+ logger.Logger().logger.error('Old didn t exists')
filepath = new_path
- with open(filepath, 'w') as fh:
+ with open(filepath, 'w', encoding='utf-8') as fh:
fh.write(_content)
- try:
- sync.pushNote(filepath)
- except:
- pass
+ # Note pushing removed to avoid the app locking up
+ # Perhaps this should be run in a different thread
+ #try:
+ # relpath = os.path.join(category, _getValidFilename(_title.strip()) + '.txt')
+ # sync.push_note(relpath)
+ #except Exception as err:
+ # logger.Logger().logger.error(str(err))
return filepath
def loadNote(path, colorize=True):
path = os.path.join(NOTESPATH, path)
- with open(path, 'r') as fh:
+ with open(path, 'r', encoding='utf-8') as fh:
try:
text = fh.read()
- if text.find('\0') > 0:
- # Probably utf-16 ... decode it to utf-8
- # as qml didn t support it well'
- text = text.decode('utf-16')
title = os.path.splitext(
os.path.basename(path))[0]
if colorize:
return _colorize((title + '\n' + text).replace('\r\n', '\n'))
else:
return (title + '\n' + text).replace('\r\n', '\n')
- except:
- return 'gurk'
+ except Exception as err:
+ raise Exception('File IO Error %s' % (str(err)))
+ raise Exception('File IO Error')
+
+def loadPreview(path, colorize=False):
+ path = os.path.join(NOTESPATH, path)
+ with open(path, 'r', encoding='utf-8') as fh:
+ try:
+ text = fh.read(512)
+ if colorize:
+ return _colorize(text.replace('\r\n', '\n'))
+ else:
+ return text.replace('\r\n', '\n')
+ except Exception as err:
+ raise Exception('File IO Error %s' % (str(err)))
raise Exception('File IO Error')
+def nextNoteFile(current, offset=1):
+ next = ''
+ notesDetails = listNotes('')
+ notes = [note['relpath'] for note in notesDetails]
+ notes.append('')
+ if current in notes:
+ next = notes[(notes.index(current) + int(offset)) % len(notes)]
+ return next
+
def listNotes(searchFilter):
path = NOTESPATH
notes = []
for root, folders, filenames in os.walk(path):
- category = os.path.relpath(root, path)
- if category == '.':
- category = ''
- if category != '.merge.sync':
- notes.extend([{'title': os.path.splitext(filename)[0],
- 'category': category,
- 'timestp':
- os.stat(
- os.path.join(
- path,
- category,
- filename)).st_mtime,
- 'timestamp':
- time.strftime('%x %X',
- time.localtime(
- os.stat(
- os.path.join(
- path,
- category,
- filename)).st_mtime)),
- 'favorited': False,
- 'path': os.path.join(path, category, filename)}
- for filename in filenames
- if filename != '.index.sync'])
+ category = os.path.relpath(root, path)
+ if category == '.':
+ category = ''
+ if category != '.merge.sync':
+ notes.extend([{'title': os.path.splitext(filename)[0],
+ 'category': category,
+ 'timestp':
+ os.stat(
+ os.path.join(
+ path,
+ category,
+ filename)).st_mtime,
+ 'timestamp':
+ time.strftime('%x %X',
+ time.localtime(
+ os.stat(
+ os.path.join(
+ path,
+ category,
+ filename)).st_mtime)),
+ 'favorited': False,
+ 'path': os.path.join(path, category, filename),
+ 'relpath': os.path.join(category, filename)}
+ for filename in filenames
+ if filename != '.index.sync'])
notes.sort(key=lambda note: (not note['favorited'],
note['category'],
@@ -300,7 +324,7 @@ def getSyncStatus():
def launchSync():
global sync
- return sync._wsync()
+ return sync.sync()
def getCategoryFromPath(path):
@@ -318,7 +342,6 @@ def createNote():
while os.path.exists(path):
inc = str(int(inc) + 1)
path = os.path.join(NOTESPATH, 'Untitled %s.txt' % inc)
- print('PATH:', path)
with open(path, 'w'):
os.utime(path, (time.time(), time.time()))
return os.path.join(NOTESPATH, 'Untitled %s.txt' % inc)
@@ -327,15 +350,15 @@ def createNote():
def getCategories():
categories = ['']
for root, folders, filenames in os.walk(NOTESPATH):
- category = os.path.relpath(root, NOTESPATH)
- if category == '.':
- category = ''
- elif filenames == []:
- continue # Remove empty category
- elif (category != '.merge.sync') and (category not in categories):
- categories.append(category)
+ category = os.path.relpath(root, NOTESPATH)
+ if category == '.':
+ category = ''
+ elif filenames == []:
+ continue # Remove empty category
+ elif (category != '.merge.sync') and (category not in categories):
+ categories.append(category)
categories.sort()
- return [{'name': category} for category in categories]
+ return [{'name': acategory} for acategory in categories]
def duplicate(path):
@@ -413,5 +436,19 @@ def publishToScriptogram(text):
text=_content)
return True
+
+def readChangeslog():
+ text = ''
+ changeLog = '/usr/share/ownNotes/datas/changelog.html'
+ if os.path.isfile(changeLog):
+ with open(changeLog) as fh:
+ text = fh.read()
+ return text
+
+def get_last_sync_datetime():
+ global sync
+ return sync.get_last_sync_datetime()
+
+
if __name__ == '__init__':
print(getCategoryFromPath(os.path.join(NOTESPATH, 'test', 'blabla.txt')))
diff --git a/Sailfish/ownNotes/python/pyaes/__init__.py b/Sailfish/ownNotes/python/pyaes/__init__.py
new file mode 100644
index 0000000..bf27aff
--- /dev/null
+++ b/Sailfish/ownNotes/python/pyaes/__init__.py
@@ -0,0 +1,52 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+
+# Supported key sizes:
+# 128-bit
+# 192-bit
+# 256-bit
+
+
+# Supported modes of operation:
+# ECB - Electronic Codebook
+# CBC - Cipher-Block Chaining
+# CFB - Cipher Feedback
+# OFB - Output Feedback
+# CTR - Counter
+
+# See the README.md for API details and general information.
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+VERSION = [1, 1, 0]
+
+from pyaes.aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter
+from pyaes.blockfeeder import Decrypter, Encrypter
diff --git a/Sailfish/ownNotes/python/pyaes/aes.py b/Sailfish/ownNotes/python/pyaes/aes.py
new file mode 100644
index 0000000..81d68ce
--- /dev/null
+++ b/Sailfish/ownNotes/python/pyaes/aes.py
@@ -0,0 +1,550 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+
+# Honestly, the best description of the modes of operations are the wonderful
+# diagrams on Wikipedia. They explain in moments what my words could never
+# achieve. Hence the inline documentation here is sparer than I'd prefer.
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+# Supported key sizes:
+# 128-bit
+# 192-bit
+# 256-bit
+
+
+# Supported modes of operation:
+# ECB - Electronic Codebook
+# CBC - Cipher-Block Chaining
+# CFB - Cipher Feedback
+# OFB - Output Feedback
+# CTR - Counter
+
+
+# See the README.md for API details and general information.
+
+
+import copy
+import struct
+
+__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB",
+ "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"]
+
+
+def _compact_word(word):
+ return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]
+
+def _string_to_bytes(text):
+ return list(ord(c) for c in text)
+
+def _bytes_to_string(text):
+ return "".join(chr(v) for v in text)
+
+
+# Based *largely* on the Rijndael implementation
+# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
+class AES(object):
+ '''Encapsulates the AES block cipher.
+
+ You generally should not need this. Use the AESModeOfOperation classes
+ below instead.'''
+
+ # Number of rounds by keysize
+ number_of_rounds = {16: 10, 24: 12, 32: 14}
+
+ # Round constant words
+ rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]
+
+ # S-box and Inverse S-box (S is for Substitution)
+ S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
+ Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ]
+
+ # Transformations for encryption
+ T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ]
+ T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ]
+ T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ]
+ T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ]
+
+ # Transformations for decryption
+ T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ]
+ T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ]
+ T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ]
+ T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ]
+
+ # Transformations for decryption key expansion
+ U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ]
+ U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ]
+ U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ]
+ U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ]
+
+ def __init__(self, key):
+
+ if len(key) not in (16, 24, 32):
+ raise ValueError('Invalid key size')
+
+ rounds = self.number_of_rounds[len(key)]
+
+ # Encryption round keys
+ self._Ke = [[0] * 4 for i in range(rounds + 1)]
+
+ # Decryption round keys
+ self._Kd = [[0] * 4 for i in range(rounds + 1)]
+
+ round_key_count = (rounds + 1) * 4
+ KC = len(key) // 4
+
+ # Convert the key into ints
+ tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in range(0, len(key), 4) ]
+
+ # Copy values into round key arrays
+ for i in range(0, KC):
+ self._Ke[i // 4][i % 4] = tk[i]
+ self._Kd[rounds - (i // 4)][i % 4] = tk[i]
+
+ # Key expansion (fips-197 section 5.2)
+ rconpointer = 0
+ t = KC
+ while t < round_key_count:
+
+ tt = tk[KC - 1]
+ tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
+ (self.S[(tt >> 8) & 0xFF] << 16) ^
+ (self.S[ tt & 0xFF] << 8) ^
+ self.S[(tt >> 24) & 0xFF] ^
+ (self.rcon[rconpointer] << 24))
+ rconpointer += 1
+
+ if KC != 8:
+ for i in range(1, KC):
+ tk[i] ^= tk[i - 1]
+
+ # Key expansion for 256-bit keys is "slightly different" (fips-197)
+ else:
+ for i in range(1, KC // 2):
+ tk[i] ^= tk[i - 1]
+ tt = tk[KC // 2 - 1]
+
+ tk[KC // 2] ^= (self.S[ tt & 0xFF] ^
+ (self.S[(tt >> 8) & 0xFF] << 8) ^
+ (self.S[(tt >> 16) & 0xFF] << 16) ^
+ (self.S[(tt >> 24) & 0xFF] << 24))
+
+ for i in range(KC // 2 + 1, KC):
+ tk[i] ^= tk[i-1]
+
+ # Copy values into round key arrays
+ j = 0
+ while j < KC and t < round_key_count:
+ self._Ke[t // 4][t % 4] = tk[j]
+ self._Kd[rounds - (t // 4)][t % 4] = tk[j]
+ j += 1
+ t += 1
+
+ # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
+ for r in range(1, rounds):
+ for j in range(0, 4):
+ tt = self._Kd[r][j]
+ self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
+ self.U2[(tt >> 16) & 0xFF] ^
+ self.U3[(tt >> 8) & 0xFF] ^
+ self.U4[ tt & 0xFF])
+
+ def encrypt(self, plaintext):
+ 'Encrypt a block of plain text using the AES block cipher.'
+
+ if len(plaintext) != 16:
+ raise ValueError('wrong block length')
+
+ rounds = len(self._Ke) - 1
+ (s1, s2, s3) = [1, 2, 3]
+ a = [0, 0, 0, 0]
+
+ # Convert plaintext to (ints ^ key)
+ t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in range(0, 4)]
+
+ # Apply round transforms
+ for r in range(1, rounds):
+ for i in range(0, 4):
+ a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^
+ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^
+ self.T4[ t[(i + s3) % 4] & 0xFF] ^
+ self._Ke[r][i])
+ t = copy.copy(a)
+
+ # The last round is special
+ result = [ ]
+ for i in range(0, 4):
+ tt = self._Ke[rounds][i]
+ result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
+
+ return result
+
+ def decrypt(self, ciphertext):
+ 'Decrypt a block of cipher text using the AES block cipher.'
+
+ if len(ciphertext) != 16:
+ raise ValueError('wrong block length')
+
+ rounds = len(self._Kd) - 1
+ (s1, s2, s3) = [3, 2, 1]
+ a = [0, 0, 0, 0]
+
+ # Convert ciphertext to (ints ^ key)
+ t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in range(0, 4)]
+
+ # Apply round transforms
+ for r in range(1, rounds):
+ for i in range(0, 4):
+ a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^
+ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^
+ self.T8[ t[(i + s3) % 4] & 0xFF] ^
+ self._Kd[r][i])
+ t = copy.copy(a)
+
+ # The last round is special
+ result = [ ]
+ for i in range(0, 4):
+ tt = self._Kd[rounds][i]
+ result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
+
+ return result
+
+
+class Counter(object):
+ '''A counter object for the Counter (CTR) mode of operation.
+
+ To create a custom counter, you can usually just override the
+ increment method.'''
+
+ def __init__(self, initial_value = 1):
+
+ # Convert the value into an array of bytes long
+ self._counter = [ ((initial_value >> i) % 256) for i in range(128 - 8, -1, -8) ]
+
+ value = property(lambda s: s._counter)
+
+ def increment(self):
+ '''Increment the counter (overflow rolls back to 0).'''
+
+ for i in range(len(self._counter) - 1, -1, -1):
+ self._counter[i] += 1
+
+ if self._counter[i] < 256: break
+
+ # Carry the one
+ self._counter[i] = 0
+
+ # Overflow
+ else:
+ self._counter = [ 0 ] * len(self._counter)
+
+
+class AESBlockModeOfOperation(object):
+ '''Super-class for AES modes of operation that require blocks.'''
+ def __init__(self, key):
+ self._aes = AES(key)
+
+ def decrypt(self, ciphertext):
+ raise Exception('not implemented')
+
+ def encrypt(self, plaintext):
+ raise Exception('not implemented')
+
+
+class AESStreamModeOfOperation(AESBlockModeOfOperation):
+ '''Super-class for AES modes of operation that are stream-ciphers.'''
+
+class AESSegmentModeOfOperation(AESStreamModeOfOperation):
+ '''Super-class for AES modes of operation that segment data.'''
+
+ segment_bytes = 16
+
+
+
+class AESModeOfOperationECB(AESBlockModeOfOperation):
+ '''AES Electronic Codebook Mode of Operation.
+
+ o Block-cipher, so data must be padded to 16 byte boundaries
+
+ Security Notes:
+ o This mode is not recommended
+ o Any two identical blocks produce identical encrypted values,
+ exposing data patterns. (See the image of Tux on wikipedia)
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1'''
+
+
+ name = "Electronic Codebook (ECB)"
+
+ def encrypt(self, plaintext):
+ if len(plaintext) != 16:
+ raise ValueError('plaintext block must be 16 bytes')
+
+ return _bytes_to_string(self._aes.encrypt(_string_to_bytes(plaintext)))
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) != 16:
+ raise ValueError('ciphertext block must be 16 bytes')
+
+ return _bytes_to_string(self._aes.decrypt(_string_to_bytes(ciphertext)))
+
+
+
+class AESModeOfOperationCBC(AESBlockModeOfOperation):
+ '''AES Cipher-Block Chaining Mode of Operation.
+
+ o The Initialization Vector (IV)
+ o Block-cipher, so data must be padded to 16 byte boundaries
+ o An incorrect initialization vector will only cause the first
+ block to be corrupt; all other blocks will be intact
+ o A corrupt bit in the cipher text will cause a block to be
+ corrupted, and the next block to be inverted, but all other
+ blocks will be intact.
+
+ Security Notes:
+ o This method (and CTR) ARE recommended.
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2'''
+
+
+ name = "Cipher-Block Chaining (CBC)"
+
+ def __init__(self, key, iv = None):
+ if iv is None:
+ self._last_cipherblock = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._last_cipherblock = _string_to_bytes(iv)
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ def encrypt(self, plaintext):
+ if len(plaintext) != 16:
+ raise ValueError('plaintext block must be 16 bytes')
+
+ precipherblock = [ (ord(p) ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ]
+ self._last_cipherblock = self._aes.encrypt(precipherblock)
+
+ return _bytes_to_string(self._last_cipherblock)
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) != 16:
+ raise ValueError('ciphertext block must be 16 bytes')
+
+ cipherblock = _string_to_bytes(ciphertext)
+ plaintext = "".join([ chr(p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ])
+ self._last_cipherblock = cipherblock
+
+ return plaintext
+
+
+
+class AESModeOfOperationCFB(AESSegmentModeOfOperation):
+ '''AES Cipher Feedback Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ but does need to be padded to segment_size
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3'''
+
+
+ name = "Cipher Feedback (CFB)"
+
+ def __init__(self, key, iv, segment_size = 1):
+ if segment_size == 0: segment_size = 1
+
+ if iv is None:
+ self._shift_register = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._shift_register = _string_to_bytes(iv)
+
+ self._segment_bytes = segment_size
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ segment_bytes = property(lambda s: s._segment_bytes)
+
+ def encrypt(self, plaintext):
+ if len(plaintext) % self._segment_bytes != 0:
+ raise ValueError('plaintext block must be a multiple of segment_size')
+
+ # Break block into segments
+ encrypted = [ ]
+ for i in range(0, len(plaintext), self._segment_bytes):
+ plaintext_segment = plaintext[i: i + self._segment_bytes]
+ xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)]
+ cipher_segment = [ (ord(p) ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ]
+
+ # Shift the top bits out and the ciphertext in
+ self._shift_register = self._shift_register[len(cipher_segment):] + cipher_segment
+
+ encrypted.extend(cipher_segment)
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) % self._segment_bytes != 0:
+ raise ValueError('ciphertext block must be a multiple of segment_size')
+
+ # Break block into segments
+ decrypted = [ ]
+ for i in range(0, len(ciphertext), self._segment_bytes):
+ cipher_segment = _string_to_bytes(ciphertext[i: i + self._segment_bytes])
+ xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)]
+ plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ]
+
+ # Shift the top bits out and the ciphertext in
+ self._shift_register = self._shift_register[len(cipher_segment):] + cipher_segment
+
+ decrypted.extend(plaintext_segment)
+
+ return _bytes_to_string(decrypted)
+
+
+
+class AESModeOfOperationOFB(AESStreamModeOfOperation):
+ '''AES Output Feedback Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ allowing arbitrary length data.
+ o A bit twiddled in the cipher text, twiddles the same bit in the
+ same bit in the plain text, which can be useful for error
+ correction techniques.
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4'''
+
+
+ name = "Output Feedback (OFB)"
+
+ def __init__(self, key, iv = None):
+ if iv is None:
+ self._last_precipherblock = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._last_precipherblock = _string_to_bytes(iv)
+
+ self._remaining_block = [ ]
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ def encrypt(self, plaintext):
+ encrypted = [ ]
+ for c in plaintext:
+ if len(self._remaining_block) == 0:
+ self._remaining_block = self._aes.encrypt(self._last_precipherblock)
+ self._last_precipherblock = [ ]
+ precipherbyte = self._remaining_block.pop(0)
+ self._last_precipherblock.append(precipherbyte)
+ cipherbyte = ord(c) ^ precipherbyte
+ encrypted.append(cipherbyte)
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, ciphertext):
+ # AES-OFB is symetric
+ return self.encrypt(ciphertext)
+
+
+
+class AESModeOfOperationCTR(AESStreamModeOfOperation):
+ '''AES Counter Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ allowing arbitrary length data.
+ o The counter must be the same size as the key size (ie. len(key))
+ o Each block independant of the other, so a corrupt byte will not
+ damage future blocks.
+ o Each block has a uniue counter value associated with it, which
+ contributes to the encrypted value, so no data patterns are
+ leaked.
+ o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and
+ Segmented Integer Counter (SIC
+
+ Security Notes:
+ o This method (and CBC) ARE recommended.
+ o Each message block is associated with a counter value which must be
+ unique for ALL messages with the same key. Otherwise security may be
+ compromised.
+
+ Also see:
+
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5
+ and Appendix B for managing the initial counter'''
+
+
+ name = "Counter (CTR)"
+
+ def __init__(self, key, counter = None):
+ AESBlockModeOfOperation.__init__(self, key)
+
+ if counter is None:
+ counter = Counter()
+
+ self._counter = counter
+ self._remaining_counter = [ ]
+
+ def encrypt(self, plaintext):
+ while len(self._remaining_counter) < len(plaintext):
+ self._remaining_counter += self._aes.encrypt(self._counter.value)
+ self._counter.increment()
+
+ encrypted = [ (ord(p) ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ]
+ self._remaining_counter = self._remaining_counter[len(encrypted):]
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, crypttext):
+ # AES-CTR is symetric
+ return self.encrypt(crypttext)
+
+
+# Simple lookup table for each mode
+AESModesOfOperation = dict(ctr = AESModeOfOperationCTR, cbc = AESModeOfOperationCBC, cfb = AESModeOfOperationCFB, ecb = AESModeOfOperationECB, ofb = AESModeOfOperationOFB)
diff --git a/Sailfish/ownNotes/python/pyaes/aes.py.bak b/Sailfish/ownNotes/python/pyaes/aes.py.bak
new file mode 100644
index 0000000..c565d95
--- /dev/null
+++ b/Sailfish/ownNotes/python/pyaes/aes.py.bak
@@ -0,0 +1,550 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+
+# Honestly, the best description of the modes of operations are the wonderful
+# diagrams on Wikipedia. They explain in moments what my words could never
+# achieve. Hence the inline documentation here is sparer than I'd prefer.
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+# Supported key sizes:
+# 128-bit
+# 192-bit
+# 256-bit
+
+
+# Supported modes of operation:
+# ECB - Electronic Codebook
+# CBC - Cipher-Block Chaining
+# CFB - Cipher Feedback
+# OFB - Output Feedback
+# CTR - Counter
+
+
+# See the README.md for API details and general information.
+
+
+import copy
+import struct
+
+__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB",
+ "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"]
+
+
+def _compact_word(word):
+ return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]
+
+def _string_to_bytes(text):
+ return list(ord(c) for c in text)
+
+def _bytes_to_string(text):
+ return "".join(chr(v) for v in text)
+
+
+# Based *largely* on the Rijndael implementation
+# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
+class AES(object):
+ '''Encapsulates the AES block cipher.
+
+ You generally should not need this. Use the AESModeOfOperation classes
+ below instead.'''
+
+ # Number of rounds by keysize
+ number_of_rounds = {16: 10, 24: 12, 32: 14}
+
+ # Round constant words
+ rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]
+
+ # S-box and Inverse S-box (S is for Substitution)
+ S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
+ Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ]
+
+ # Transformations for encryption
+ T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ]
+ T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ]
+ T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ]
+ T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ]
+
+ # Transformations for decryption
+ T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ]
+ T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ]
+ T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ]
+ T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ]
+
+ # Transformations for decryption key expansion
+ U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ]
+ U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ]
+ U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ]
+ U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ]
+
+ def __init__(self, key):
+
+ if len(key) not in (16, 24, 32):
+ raise ValueError('Invalid key size')
+
+ rounds = self.number_of_rounds[len(key)]
+
+ # Encryption round keys
+ self._Ke = [[0] * 4 for i in xrange(rounds + 1)]
+
+ # Decryption round keys
+ self._Kd = [[0] * 4 for i in xrange(rounds + 1)]
+
+ round_key_count = (rounds + 1) * 4
+ KC = len(key) / 4
+
+ # Convert the key into ints
+ tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ]
+
+ # Copy values into round key arrays
+ for i in xrange(0, KC):
+ self._Ke[i / 4][i % 4] = tk[i]
+ self._Kd[rounds - (i / 4)][i % 4] = tk[i]
+
+ # Key expansion (fips-197 section 5.2)
+ rconpointer = 0
+ t = KC
+ while t < round_key_count:
+
+ tt = tk[KC - 1]
+ tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
+ (self.S[(tt >> 8) & 0xFF] << 16) ^
+ (self.S[ tt & 0xFF] << 8) ^
+ self.S[(tt >> 24) & 0xFF] ^
+ (self.rcon[rconpointer] << 24))
+ rconpointer += 1
+
+ if KC != 8:
+ for i in xrange(1, KC):
+ tk[i] ^= tk[i - 1]
+
+ # Key expansion for 256-bit keys is "slightly different" (fips-197)
+ else:
+ for i in xrange(1, KC / 2):
+ tk[i] ^= tk[i - 1]
+ tt = tk[KC / 2 - 1]
+
+ tk[KC / 2] ^= (self.S[ tt & 0xFF] ^
+ (self.S[(tt >> 8) & 0xFF] << 8) ^
+ (self.S[(tt >> 16) & 0xFF] << 16) ^
+ (self.S[(tt >> 24) & 0xFF] << 24))
+
+ for i in xrange(KC / 2 + 1, KC):
+ tk[i] ^= tk[i-1]
+
+ # Copy values into round key arrays
+ j = 0
+ while j < KC and t < round_key_count:
+ self._Ke[t / 4][t % 4] = tk[j]
+ self._Kd[rounds - (t / 4)][t % 4] = tk[j]
+ j += 1
+ t += 1
+
+ # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
+ for r in xrange(1, rounds):
+ for j in xrange(0, 4):
+ tt = self._Kd[r][j]
+ self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
+ self.U2[(tt >> 16) & 0xFF] ^
+ self.U3[(tt >> 8) & 0xFF] ^
+ self.U4[ tt & 0xFF])
+
+ def encrypt(self, plaintext):
+ 'Encrypt a block of plain text using the AES block cipher.'
+
+ if len(plaintext) != 16:
+ raise ValueError('wrong block length')
+
+ rounds = len(self._Ke) - 1
+ (s1, s2, s3) = [1, 2, 3]
+ a = [0, 0, 0, 0]
+
+ # Convert plaintext to (ints ^ key)
+ t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)]
+
+ # Apply round transforms
+ for r in xrange(1, rounds):
+ for i in xrange(0, 4):
+ a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^
+ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^
+ self.T4[ t[(i + s3) % 4] & 0xFF] ^
+ self._Ke[r][i])
+ t = copy.copy(a)
+
+ # The last round is special
+ result = [ ]
+ for i in xrange(0, 4):
+ tt = self._Ke[rounds][i]
+ result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
+
+ return result
+
+ def decrypt(self, ciphertext):
+ 'Decrypt a block of cipher text using the AES block cipher.'
+
+ if len(ciphertext) != 16:
+ raise ValueError('wrong block length')
+
+ rounds = len(self._Kd) - 1
+ (s1, s2, s3) = [3, 2, 1]
+ a = [0, 0, 0, 0]
+
+ # Convert ciphertext to (ints ^ key)
+ t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]
+
+ # Apply round transforms
+ for r in xrange(1, rounds):
+ for i in xrange(0, 4):
+ a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^
+ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^
+ self.T8[ t[(i + s3) % 4] & 0xFF] ^
+ self._Kd[r][i])
+ t = copy.copy(a)
+
+ # The last round is special
+ result = [ ]
+ for i in xrange(0, 4):
+ tt = self._Kd[rounds][i]
+ result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
+
+ return result
+
+
+class Counter(object):
+ '''A counter object for the Counter (CTR) mode of operation.
+
+ To create a custom counter, you can usually just override the
+ increment method.'''
+
+ def __init__(self, initial_value = 1):
+
+ # Convert the value into an array of bytes long
+ self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ]
+
+ value = property(lambda s: s._counter)
+
+ def increment(self):
+ '''Increment the counter (overflow rolls back to 0).'''
+
+ for i in xrange(len(self._counter) - 1, -1, -1):
+ self._counter[i] += 1
+
+ if self._counter[i] < 256: break
+
+ # Carry the one
+ self._counter[i] = 0
+
+ # Overflow
+ else:
+ self._counter = [ 0 ] * len(self._counter)
+
+
+class AESBlockModeOfOperation(object):
+ '''Super-class for AES modes of operation that require blocks.'''
+ def __init__(self, key):
+ self._aes = AES(key)
+
+ def decrypt(self, ciphertext):
+ raise Exception('not implemented')
+
+ def encrypt(self, plaintext):
+ raise Exception('not implemented')
+
+
+class AESStreamModeOfOperation(AESBlockModeOfOperation):
+ '''Super-class for AES modes of operation that are stream-ciphers.'''
+
+class AESSegmentModeOfOperation(AESStreamModeOfOperation):
+ '''Super-class for AES modes of operation that segment data.'''
+
+ segment_bytes = 16
+
+
+
+class AESModeOfOperationECB(AESBlockModeOfOperation):
+ '''AES Electronic Codebook Mode of Operation.
+
+ o Block-cipher, so data must be padded to 16 byte boundaries
+
+ Security Notes:
+ o This mode is not recommended
+ o Any two identical blocks produce identical encrypted values,
+ exposing data patterns. (See the image of Tux on wikipedia)
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1'''
+
+
+ name = "Electronic Codebook (ECB)"
+
+ def encrypt(self, plaintext):
+ if len(plaintext) != 16:
+ raise ValueError('plaintext block must be 16 bytes')
+
+ return _bytes_to_string(self._aes.encrypt(_string_to_bytes(plaintext)))
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) != 16:
+ raise ValueError('ciphertext block must be 16 bytes')
+
+ return _bytes_to_string(self._aes.decrypt(_string_to_bytes(ciphertext)))
+
+
+
+class AESModeOfOperationCBC(AESBlockModeOfOperation):
+ '''AES Cipher-Block Chaining Mode of Operation.
+
+ o The Initialization Vector (IV)
+ o Block-cipher, so data must be padded to 16 byte boundaries
+ o An incorrect initialization vector will only cause the first
+ block to be corrupt; all other blocks will be intact
+ o A corrupt bit in the cipher text will cause a block to be
+ corrupted, and the next block to be inverted, but all other
+ blocks will be intact.
+
+ Security Notes:
+ o This method (and CTR) ARE recommended.
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2'''
+
+
+ name = "Cipher-Block Chaining (CBC)"
+
+ def __init__(self, key, iv = None):
+ if iv is None:
+ self._last_cipherblock = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._last_cipherblock = _string_to_bytes(iv)
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ def encrypt(self, plaintext):
+ if len(plaintext) != 16:
+ raise ValueError('plaintext block must be 16 bytes')
+
+ precipherblock = [ (ord(p) ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ]
+ self._last_cipherblock = self._aes.encrypt(precipherblock)
+
+ return _bytes_to_string(self._last_cipherblock)
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) != 16:
+ raise ValueError('ciphertext block must be 16 bytes')
+
+ cipherblock = _string_to_bytes(ciphertext)
+ plaintext = "".join([ chr(p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ])
+ self._last_cipherblock = cipherblock
+
+ return plaintext
+
+
+
+class AESModeOfOperationCFB(AESSegmentModeOfOperation):
+ '''AES Cipher Feedback Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ but does need to be padded to segment_size
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3'''
+
+
+ name = "Cipher Feedback (CFB)"
+
+ def __init__(self, key, iv, segment_size = 1):
+ if segment_size == 0: segment_size = 1
+
+ if iv is None:
+ self._shift_register = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._shift_register = _string_to_bytes(iv)
+
+ self._segment_bytes = segment_size
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ segment_bytes = property(lambda s: s._segment_bytes)
+
+ def encrypt(self, plaintext):
+ if len(plaintext) % self._segment_bytes != 0:
+ raise ValueError('plaintext block must be a multiple of segment_size')
+
+ # Break block into segments
+ encrypted = [ ]
+ for i in xrange(0, len(plaintext), self._segment_bytes):
+ plaintext_segment = plaintext[i: i + self._segment_bytes]
+ xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)]
+ cipher_segment = [ (ord(p) ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ]
+
+ # Shift the top bits out and the ciphertext in
+ self._shift_register = self._shift_register[len(cipher_segment):] + cipher_segment
+
+ encrypted.extend(cipher_segment)
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) % self._segment_bytes != 0:
+ raise ValueError('ciphertext block must be a multiple of segment_size')
+
+ # Break block into segments
+ decrypted = [ ]
+ for i in xrange(0, len(ciphertext), self._segment_bytes):
+ cipher_segment = _string_to_bytes(ciphertext[i: i + self._segment_bytes])
+ xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)]
+ plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ]
+
+ # Shift the top bits out and the ciphertext in
+ self._shift_register = self._shift_register[len(cipher_segment):] + cipher_segment
+
+ decrypted.extend(plaintext_segment)
+
+ return _bytes_to_string(decrypted)
+
+
+
+class AESModeOfOperationOFB(AESStreamModeOfOperation):
+ '''AES Output Feedback Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ allowing arbitrary length data.
+ o A bit twiddled in the cipher text, twiddles the same bit in the
+ same bit in the plain text, which can be useful for error
+ correction techniques.
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4'''
+
+
+ name = "Output Feedback (OFB)"
+
+ def __init__(self, key, iv = None):
+ if iv is None:
+ self._last_precipherblock = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._last_precipherblock = _string_to_bytes(iv)
+
+ self._remaining_block = [ ]
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ def encrypt(self, plaintext):
+ encrypted = [ ]
+ for c in plaintext:
+ if len(self._remaining_block) == 0:
+ self._remaining_block = self._aes.encrypt(self._last_precipherblock)
+ self._last_precipherblock = [ ]
+ precipherbyte = self._remaining_block.pop(0)
+ self._last_precipherblock.append(precipherbyte)
+ cipherbyte = ord(c) ^ precipherbyte
+ encrypted.append(cipherbyte)
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, ciphertext):
+ # AES-OFB is symetric
+ return self.encrypt(ciphertext)
+
+
+
+class AESModeOfOperationCTR(AESStreamModeOfOperation):
+ '''AES Counter Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ allowing arbitrary length data.
+ o The counter must be the same size as the key size (ie. len(key))
+ o Each block independant of the other, so a corrupt byte will not
+ damage future blocks.
+ o Each block has a uniue counter value associated with it, which
+ contributes to the encrypted value, so no data patterns are
+ leaked.
+ o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and
+ Segmented Integer Counter (SIC
+
+ Security Notes:
+ o This method (and CBC) ARE recommended.
+ o Each message block is associated with a counter value which must be
+ unique for ALL messages with the same key. Otherwise security may be
+ compromised.
+
+ Also see:
+
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5
+ and Appendix B for managing the initial counter'''
+
+
+ name = "Counter (CTR)"
+
+ def __init__(self, key, counter = None):
+ AESBlockModeOfOperation.__init__(self, key)
+
+ if counter is None:
+ counter = Counter()
+
+ self._counter = counter
+ self._remaining_counter = [ ]
+
+ def encrypt(self, plaintext):
+ while len(self._remaining_counter) < len(plaintext):
+ self._remaining_counter += self._aes.encrypt(self._counter.value)
+ self._counter.increment()
+
+ encrypted = [ (ord(p) ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ]
+ self._remaining_counter = self._remaining_counter[len(encrypted):]
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, crypttext):
+ # AES-CTR is symetric
+ return self.encrypt(crypttext)
+
+
+# Simple lookup table for each mode
+AESModesOfOperation = dict(ctr = AESModeOfOperationCTR, cbc = AESModeOfOperationCBC, cfb = AESModeOfOperationCFB, ecb = AESModeOfOperationECB, ofb = AESModeOfOperationOFB)
diff --git a/Sailfish/ownNotes/python/pyaes/blockfeeder.py b/Sailfish/ownNotes/python/pyaes/blockfeeder.py
new file mode 100644
index 0000000..b0d3899
--- /dev/null
+++ b/Sailfish/ownNotes/python/pyaes/blockfeeder.py
@@ -0,0 +1,158 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+from pyaes.aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation
+from pyaes.util import append_PKCS7_padding, strip_PKCS7_padding
+
+# First we inject three functions to each of the modes of operations
+#
+# _can_consume(size)
+# - Given a size, determine how many bytes could be consumed in
+# a single call to either the decrypt or encrypt method
+#
+# _final_encrypt(data)
+# - call and return encrypt on this (last) chunk of data,
+# padding as necessary; this will always be at least 16
+# bytes unless the total incoming input was less than 16
+# bytes
+#
+# _final_decrypt(data)
+# - same as _final_encrypt except for decrypt, for
+# stripping off padding
+#
+
+
+# ECB and CBC are block-only ciphers
+
+def _block_can_consume(self, size):
+ if size >= 16: return 16
+ return 0
+
+# After padding, we may have more than one block
+def _block_final_encrypt(self, data):
+ data = append_PKCS7_padding(data)
+ if len(data) == 32:
+ return self.encrypt(data[:16]) + self.encrypt(data[16:])
+ return self.encrypt(data)
+
+def _block_final_decrypt(self, data):
+ return strip_PKCS7_padding(self.decrypt(data))
+
+AESBlockModeOfOperation._can_consume = _block_can_consume
+AESBlockModeOfOperation._final_encrypt = _block_final_encrypt
+AESBlockModeOfOperation._final_decrypt = _block_final_decrypt
+
+
+
+# CFB is a segment cipher
+
+def _segment_can_consume(self, size):
+ return self.segment_bytes * int(size // self.segment_bytes)
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_encrypt(self, data):
+ padded = data + (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+ return self.encrypt(padded)[:len(data)]
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_decrypt(self, data):
+ padded = data + (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+ return self.decrypt(padded)[:len(data)]
+
+AESSegmentModeOfOperation._can_consume = _segment_can_consume
+AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt
+AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt
+
+
+
+# OFB and CTR are stream ciphers
+
+def _stream_can_consume(self, size):
+ return size
+
+def _stream_final_encrypt(self, data):
+ return self.encrypt(data)
+
+def _stream_final_decrypt(self, data):
+ return self.decrypt(data)
+
+AESStreamModeOfOperation._can_consume = _stream_can_consume
+AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt
+AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt
+
+
+
+class BlockFeeder(object):
+ '''The super-class for objects to handle chunking a stream of bytes
+ into the appropriate block size for the underlying mode of operation
+ and applying (or stripping) padding, as necessary.'''
+
+ def __init__(self, mode, feed, final):
+ self._mode = mode
+ self._feed = feed
+ self._final = final
+ self._buffer = ""
+
+ def feed(self, data = None):
+ '''Provide bytes to encrypt (or decrypt), returning any bytes
+ possible from this or any previous calls to feed.
+
+ Call with None or an empty string to flush the mode of
+ operation and return any final bytes; no further calls to
+ feed may be made.'''
+
+ if self._buffer is None:
+ raise ValueError('already finished feeder')
+
+ # Finalize; process the spare bytes we were keeping
+ if not data:
+ result = self._final(self._buffer)
+ self._buffer = None
+ return result
+
+ self._buffer += data
+
+ # We keep 16 bytes around so we can determine padding
+ result = ''
+ while len(self._buffer) > 16:
+ can_consume = self._mode._can_consume(len(self._buffer) - 16)
+ if can_consume == 0: break
+ result += self._feed(self._buffer[:can_consume])
+ self._buffer = self._buffer[can_consume:]
+
+ return result
+
+
+class Encrypter(BlockFeeder):
+ 'Accepts bytes of plaintext and returns encrypted ciphertext.'
+
+ def __init__(self, mode):
+ BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt)
+
+
+class Decrypter(BlockFeeder):
+ 'Accepts bytes of ciphertext and returns decrypted plaintext.'
+
+ def __init__(self, mode):
+ BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt)
+
diff --git a/Sailfish/ownNotes/python/pyaes/util.py b/Sailfish/ownNotes/python/pyaes/util.py
new file mode 100644
index 0000000..72f87f9
--- /dev/null
+++ b/Sailfish/ownNotes/python/pyaes/util.py
@@ -0,0 +1,43 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+def append_PKCS7_padding(data):
+ pad = 16 - (len(data) % 16)
+ return data + pad * chr(pad)
+
+def strip_PKCS7_padding(data):
+ if len(data) % 16 != 0:
+ raise ValueError("invalid length")
+
+ pad = ord(data[-1])
+
+ if pad > 16:
+ raise ValueError("invalid padding byte")
+
+ return data[:-pad]
+
+if __name__ == '__main__':
+ for i in range(0, 17):
+ data = 'A' * i
+ padded = append_PKCS7_padding(data)
+ print(repr(padded), strip_PKCS7_padding(padded) == data)
diff --git a/Sailfish/ownNotes/python/pyaes/util.py.bak b/Sailfish/ownNotes/python/pyaes/util.py.bak
new file mode 100644
index 0000000..dd13bda
--- /dev/null
+++ b/Sailfish/ownNotes/python/pyaes/util.py.bak
@@ -0,0 +1,43 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+def append_PKCS7_padding(data):
+ pad = 16 - (len(data) % 16)
+ return data + pad * chr(pad)
+
+def strip_PKCS7_padding(data):
+ if len(data) % 16 != 0:
+ raise ValueError("invalid length")
+
+ pad = ord(data[-1])
+
+ if pad > 16:
+ raise ValueError("invalid padding byte")
+
+ return data[:-pad]
+
+if __name__ == '__main__':
+ for i in xrange(0, 17):
+ data = 'A' * i
+ padded = append_PKCS7_padding(data)
+ print repr(padded), strip_PKCS7_padding(padded) == data
diff --git a/Sailfish/ownNotes/python/qp_xml.py b/Sailfish/ownNotes/python/qp_xml.py
deleted file mode 100755
index 92976fc..0000000
--- a/Sailfish/ownNotes/python/qp_xml.py
+++ /dev/null
@@ -1,246 +0,0 @@
-# pylint:
-# disable-msg=W0311,E1101,E1103,W0201,C0103,W0622,W0402,W0706,R0911,W0613,W0612,R0912,W0141,C0111,C0121
-
-# qp_xml: Quick Parsing for XML
-#
-# Written by Greg Stein. Public Domain.
-# No Copyright, no Rights Reserved, and no Warranties.
-#
-# This module is maintained by Greg and is available as part of the XML-SIG
-# distribution. This module and its changelog can be fetched at:
-# http://www.lyra.org/cgi-bin/viewcvs.cgi/xml/xml/utils/qp_xml.py
-#
-# Additional information can be found on Greg's Python page at:
-# http://www.lyra.org/greg/python/
-#
-# This module was added to the XML-SIG distribution on February 14, 2000.
-# As part of that distribution, it falls under the XML distribution license.
-#
-
-import string
-from xml.parsers import expat
-
-
-error = __name__ + '.error'
-
-
-#
-# The parsing class. Instantiate and pass a string/file to .parse()
-#
-class Parser:
-
- def __init__(self):
- self.reset()
-
- def reset(self):
- self.root = None
- self.cur_elem = None
-
- def find_prefix(self, prefix):
- elem = self.cur_elem
- while elem:
- if prefix in elem.ns_scope:
- return elem.ns_scope[prefix]
- elem = elem.parent
-
- if prefix == '':
- return '' # empty URL for "no namespace"
-
- return None
-
- def process_prefix(self, name, use_default):
- idx = string.find(name, ':')
- if idx == -1:
- if use_default:
- return self.find_prefix(''), name
- return '', name # no namespace
-
- if string.lower(name[:3]) == 'xml':
- return '', name # name is reserved by XML. don't break out a NS.
-
- ns = self.find_prefix(name[:idx])
- if ns is None:
- raise error('namespace prefix ("%s") not found' % name[:idx])
-
- return ns, name[idx + 1:]
-
- def start(self, name, attrs):
- elem = _element(name=name, lang=None, parent=None,
- children=[], ns_scope={}, attrs={},
- first_cdata='', following_cdata='')
-
- if self.cur_elem:
- elem.parent = self.cur_elem
- elem.parent.children.append(elem)
- self.cur_elem = elem
- else:
- self.cur_elem = self.root = elem
-
- work_attrs = []
-
- # scan for namespace declarations (and xml:lang while we're at it)
- for name, value in list(attrs.items()):
- if name == 'xmlns':
- elem.ns_scope[''] = value
- elif name[:6] == 'xmlns:':
- elem.ns_scope[name[6:]] = value
- elif name == 'xml:lang':
- elem.lang = value
- else:
- work_attrs.append((name, value))
-
- # inherit xml:lang from parent
- if elem.lang is None and elem.parent:
- elem.lang = elem.parent.lang
-
- # process prefix of the element name
- elem.ns, elem.name = self.process_prefix(elem.name, 1)
-
- # process attributes' namespace prefixes
- for name, value in work_attrs:
- elem.attrs[self.process_prefix(name, 0)] = value
-
- def end(self, name):
- parent = self.cur_elem.parent
-
- del self.cur_elem.ns_scope
- del self.cur_elem.parent
-
- self.cur_elem = parent
-
- def cdata(self, data):
- elem = self.cur_elem
- if elem.children:
- last = elem.children[-1]
- last.following_cdata = last.following_cdata + data
- else:
- elem.first_cdata = elem.first_cdata + data
-
- def parse(self, input):
- self.reset()
-
- p = expat.ParserCreate()
- p.StartElementHandler = self.start
- p.EndElementHandler = self.end
- p.CharacterDataHandler = self.cdata
-
- try:
- if type(input) == type(''):
- p.Parse(input, 1)
- else:
- while 1:
- s = input.read(_BLOCKSIZE)
- if not s:
- p.Parse('', 1)
- break
-
- p.Parse(s, 0)
-
- finally:
- if self.root:
- _clean_tree(self.root)
-
- return self.root
-
-
-#
-# handy function for dumping a tree that is returned by Parser
-#
-def dump(f, root):
- f.write('\n')
- namespaces = _collect_ns(root)
- _dump_recurse(f, root, namespaces, dump_ns=1)
- f.write('\n')
-
-
-#
-# This function returns the element's CDATA. Note: this is not recursive --
-# it only returns the CDATA immediately within the element, excluding the
-# CDATA in child elements.
-#
-def textof(elem):
- return elem.textof()
-
-
-#
-#
-# private stuff for qp_xml
-#
-
-_BLOCKSIZE = 16384 # chunk size for parsing input
-
-
-class _element:
-
- def __init__(self, **kw):
- self.__dict__.update(kw)
-
- def textof(self):
- '''Return the CDATA of this element.
-
- Note: this is not recursive -- it only returns the CDATA immediately
- within the element, excluding the CDATA in child elements.
- '''
- s = self.first_cdata
- for child in self.children:
- s = s + child.following_cdata
- return s
-
- def find(self, name, ns=''):
- for elem in self.children:
- if elem.name == name and elem.ns == ns:
- return elem
- return None
-
-
-def _clean_tree(elem):
- elem.parent = None
- del elem.parent
- list(map(_clean_tree, elem.children))
-
-
-def _collect_recurse(elem, dict):
- dict[elem.ns] = None
- for ns, name in list(elem.attrs.keys()):
- dict[ns] = None
- for child in elem.children:
- _collect_recurse(child, dict)
-
-
-def _collect_ns(elem):
- "Collect all namespaces into a NAMESPACE -> PREFIX mapping."
- d = {'': None}
- _collect_recurse(elem, d)
- del d[''] # make sure we don't pick up no-namespace entries
- keys = list(d.keys())
- for i in range(len(keys)):
- d[keys[i]] = i
- return d
-
-
-def _dump_recurse(f, elem, namespaces, lang=None, dump_ns=0):
- if elem.ns:
- f.write('' + elem.first_cdata)
- for child in elem.children:
- _dump_recurse(f, child, namespaces, elem.lang)
- f.write(child.following_cdata)
- if elem.ns:
- f.write('' % (namespaces[elem.ns], elem.name))
- else:
- f.write('%s>' % elem.name)
- else:
- f.write('/>')
diff --git a/Sailfish/ownNotes/python/requests/__init__.py b/Sailfish/ownNotes/python/requests/__init__.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/adapters.py b/Sailfish/ownNotes/python/requests/adapters.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/api.py b/Sailfish/ownNotes/python/requests/api.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/auth.py b/Sailfish/ownNotes/python/requests/auth.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/cacert.pem b/Sailfish/ownNotes/python/requests/cacert.pem
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/certs.py b/Sailfish/ownNotes/python/requests/certs.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/compat.py b/Sailfish/ownNotes/python/requests/compat.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/cookies.py b/Sailfish/ownNotes/python/requests/cookies.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/exceptions.py b/Sailfish/ownNotes/python/requests/exceptions.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/hooks.py b/Sailfish/ownNotes/python/requests/hooks.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/models.py b/Sailfish/ownNotes/python/requests/models.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/__init__.py b/Sailfish/ownNotes/python/requests/packages/__init__.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/__init__.py b/Sailfish/ownNotes/python/requests/packages/chardet/__init__.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/big5freq.py b/Sailfish/ownNotes/python/requests/packages/chardet/big5freq.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/big5prober.py b/Sailfish/ownNotes/python/requests/packages/chardet/big5prober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/chardistribution.py b/Sailfish/ownNotes/python/requests/packages/chardet/chardistribution.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/charsetgroupprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/charsetgroupprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/charsetprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/charsetprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/codingstatemachine.py b/Sailfish/ownNotes/python/requests/packages/chardet/codingstatemachine.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/compat.py b/Sailfish/ownNotes/python/requests/packages/chardet/compat.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/constants.py b/Sailfish/ownNotes/python/requests/packages/chardet/constants.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/cp949prober.py b/Sailfish/ownNotes/python/requests/packages/chardet/cp949prober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/escprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/escprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/escsm.py b/Sailfish/ownNotes/python/requests/packages/chardet/escsm.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/eucjpprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/eucjpprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/euckrfreq.py b/Sailfish/ownNotes/python/requests/packages/chardet/euckrfreq.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/euckrprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/euckrprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/euctwfreq.py b/Sailfish/ownNotes/python/requests/packages/chardet/euctwfreq.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/euctwprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/euctwprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/gb2312freq.py b/Sailfish/ownNotes/python/requests/packages/chardet/gb2312freq.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/gb2312prober.py b/Sailfish/ownNotes/python/requests/packages/chardet/gb2312prober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/hebrewprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/hebrewprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/jisfreq.py b/Sailfish/ownNotes/python/requests/packages/chardet/jisfreq.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/jpcntx.py b/Sailfish/ownNotes/python/requests/packages/chardet/jpcntx.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/langbulgarianmodel.py b/Sailfish/ownNotes/python/requests/packages/chardet/langbulgarianmodel.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/langcyrillicmodel.py b/Sailfish/ownNotes/python/requests/packages/chardet/langcyrillicmodel.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/langgreekmodel.py b/Sailfish/ownNotes/python/requests/packages/chardet/langgreekmodel.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/langhebrewmodel.py b/Sailfish/ownNotes/python/requests/packages/chardet/langhebrewmodel.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/langhungarianmodel.py b/Sailfish/ownNotes/python/requests/packages/chardet/langhungarianmodel.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/langthaimodel.py b/Sailfish/ownNotes/python/requests/packages/chardet/langthaimodel.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/latin1prober.py b/Sailfish/ownNotes/python/requests/packages/chardet/latin1prober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/mbcharsetprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/mbcharsetprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/mbcsgroupprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/mbcsgroupprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/mbcssm.py b/Sailfish/ownNotes/python/requests/packages/chardet/mbcssm.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/sbcharsetprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/sbcharsetprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/sbcsgroupprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/sbcsgroupprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/sjisprober.py b/Sailfish/ownNotes/python/requests/packages/chardet/sjisprober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/universaldetector.py b/Sailfish/ownNotes/python/requests/packages/chardet/universaldetector.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/chardet/utf8prober.py b/Sailfish/ownNotes/python/requests/packages/chardet/utf8prober.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/__init__.py b/Sailfish/ownNotes/python/requests/packages/urllib3/__init__.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/_collections.py b/Sailfish/ownNotes/python/requests/packages/urllib3/_collections.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/connection.py b/Sailfish/ownNotes/python/requests/packages/urllib3/connection.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/connectionpool.py b/Sailfish/ownNotes/python/requests/packages/urllib3/connectionpool.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/contrib/__init__.py b/Sailfish/ownNotes/python/requests/packages/urllib3/contrib/__init__.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/contrib/ntlmpool.py b/Sailfish/ownNotes/python/requests/packages/urllib3/contrib/ntlmpool.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/contrib/pyopenssl.py b/Sailfish/ownNotes/python/requests/packages/urllib3/contrib/pyopenssl.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/exceptions.py b/Sailfish/ownNotes/python/requests/packages/urllib3/exceptions.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/fields.py b/Sailfish/ownNotes/python/requests/packages/urllib3/fields.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/filepost.py b/Sailfish/ownNotes/python/requests/packages/urllib3/filepost.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/packages/__init__.py b/Sailfish/ownNotes/python/requests/packages/urllib3/packages/__init__.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/packages/ordered_dict.py b/Sailfish/ownNotes/python/requests/packages/urllib3/packages/ordered_dict.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/packages/six.py b/Sailfish/ownNotes/python/requests/packages/urllib3/packages/six.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py b/Sailfish/ownNotes/python/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py b/Sailfish/ownNotes/python/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/poolmanager.py b/Sailfish/ownNotes/python/requests/packages/urllib3/poolmanager.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/request.py b/Sailfish/ownNotes/python/requests/packages/urllib3/request.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/response.py b/Sailfish/ownNotes/python/requests/packages/urllib3/response.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/packages/urllib3/util.py b/Sailfish/ownNotes/python/requests/packages/urllib3/util.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/sessions.py b/Sailfish/ownNotes/python/requests/sessions.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/status_codes.py b/Sailfish/ownNotes/python/requests/status_codes.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/structures.py b/Sailfish/ownNotes/python/requests/structures.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/requests/utils.py b/Sailfish/ownNotes/python/requests/utils.py
old mode 100644
new mode 100755
diff --git a/Sailfish/ownNotes/python/settings.py b/Sailfish/ownNotes/python/settings.py
index 3302b5d..aa9a8cb 100755
--- a/Sailfish/ownNotes/python/settings.py
+++ b/Sailfish/ownNotes/python/settings.py
@@ -15,6 +15,9 @@
import os
import json
+#import SimpleAES
+import hashlib
+import pyaes
class Settings(object):
@@ -34,15 +37,17 @@ def _get_defaults(self):
'fontsize': 18,
'fontfamily': 'Nokia Pure',
'header': True,
+ 'covernote' : '',
},
'WebDav': {
'url': 'https://owncloud.khertan.net/remote.php/webdav/',
'login': 'demo',
- 'password': 'demo',
+ 'password': '',
'remoteFolder': 'Notes',
'merge': True,
'nosslcheck': False,
- 'startupsync': False
+ 'startupsync': False,
+ 'debug': False
},
'KhtCms': {
'url': 'https://khertan.net/publish_api.php',
@@ -69,7 +74,6 @@ def _read(self,):
with open(os.path.expanduser('~/.ownnotes.conf'), 'r') \
as configfile:
jsondata = json.load(configfile)
- print(jsondata)
for k, v in list(jsondata.items()):
if type(v) is dict:
self._settings[k].update(v)
@@ -77,19 +81,41 @@ def _read(self,):
self._settings[k] = v
else:
self._write()
- print(self._settings)
def get(self, section, option):
if not self._settings:
self._read()
+
+ #Obscure ...
+ if (section == 'WebDav' and option == 'password'):
+ if os.path.exists('/etc/machine-id'):
+ with open('/etc/machine-id', 'r') as fh:
+ aes = pyaes.AESModeOfOperationCTR(
+ hashlib.sha256(fh.read().encode('utf-8')).digest())
+ return aes.decrypt(self._settings[section][option])
+
return self._settings[section][option]
def set(self, section, option, value):
if not self._settings:
self._read()
- self._settings[section][option] = value
+
+ if (section == 'WebDav' and option == 'password'):
+ if os.path.exists('/etc/machine-id'):
+ with open('/etc/machine-id', 'r') as fh:
+ aes = pyaes.AESModeOfOperationCTR(
+ hashlib.sha256(fh.read().encode('utf-8')).digest())
+ self._settings[section][option] = \
+ aes.encrypt(value)
+ else:
+ self._settings[section][option] = value
+ else:
+ self._settings[section][option] = value
+
self._write()
if __name__ == '__main__':
Settings()
+ #Settings().set('WebDav', 'password', 'test')
+ #assert(Settings().get('WebDav', 'password') == 'test')
diff --git a/Sailfish/ownNotes/python/sync.py b/Sailfish/ownNotes/python/sync.py
index df2e520..2f0ef9b 100755
--- a/Sailfish/ownNotes/python/sync.py
+++ b/Sailfish/ownNotes/python/sync.py
@@ -19,14 +19,12 @@
from settings import Settings
import time
import json
-#import logging
-
-#from webdav.Connection import WebdavError
-#import http
import rfc822py3 as rfc822
import datetime
import requests
import tinydav
+import logger
+from fakelock import fakelock
INVALID_FILENAME_CHARS = '\/:*?"<>|'
@@ -46,7 +44,7 @@ class NetworkError(Exception):
def _getValidFilename(filepath):
dirname, filename = os.path.dirname(filepath), os.path.basename(filepath)
return os.path.join(dirname, ''.join(car for car in filename
- if car not in INVALID_FILENAME_CHARS))
+ if car not in INVALID_FILENAME_CHARS))
def local2utc(secs):
@@ -67,9 +65,10 @@ def webdavPathJoin(path, *args):
class localClient(object):
- def __init__(self,):
+ def __init__(self, logger):
self.basepath = os.path.expanduser('~/.ownnotes/') # Use xdg env
self._check_notes_folder()
+ self.logger = logger
def _check_notes_folder(self,):
if not os.path.exists(self.basepath):
@@ -125,20 +124,20 @@ def get_size(self, relpath):
class WebdavClient(object):
- def __init__(self,):
+ def __init__(self, logger):
settings = Settings()
self.url = settings.get('WebDav', 'url')
-
self.login = settings.get('WebDav', 'login')
self.passwd = settings.get('WebDav', 'password')
self.basepath = requests.utils.urlparse(self.url).path
self.remotefolder = settings.get('WebDav', 'remoteFolder')
self.nosslcheck = settings.get('WebDav', 'nosslcheck')
- self.timedelta = None
+ self.time_delta = None
self.wc = None
self.locktoken = None
+ self.logger = logger
def connect(self,):
@@ -150,7 +149,7 @@ def connect(self,):
self.wc.setbasicauth(self.login.encode('utf-8'),
self.passwd.encode('utf-8'))
- time_delta = None
+ self.time_delta = None
local_time = datetime.datetime.utcnow()
@@ -161,19 +160,23 @@ def connect(self,):
remote_datetime = \
rfc822.parsedate(response)
- self.timedelta = time.mktime(local_time.utctimetuple()) \
+ self.time_delta = time.mktime(local_time.utctimetuple()) \
- time.mktime(remote_datetime)
self._check_notes_folder()
- return time_delta
+ self.logger.logger.debug('Response : %s',
+ response)
+
+ self.logger.logger.info('Connected to %s : Time delta %s',
+ urlparsed.netloc, str(self.time_delta))
+
+ return self.time_delta
def _check_notes_folder(self,):
response = self.wc.propfind(uri=self.basepath,
names=True,
depth=1)
- print(type(response))
- print(dir(response))
ownnotes_folder_exists = False
ownnotes_remote_folder = self.get_abspath('')
if response.real != 207:
@@ -181,14 +184,21 @@ def _check_notes_folder(self,):
else:
is_connected = True
for res in response:
+ self.logger.logger.debug('Check Notes Folder %s : %s',
+ ownnotes_remote_folder, res.href)
if (res.href == ownnotes_remote_folder):
ownnotes_folder_exists = True
if not ownnotes_folder_exists:
- self.wc.mkcol(ownnotes_remote_folder)
+ with self.locktoken:
+ self.wc.mkcol(ownnotes_remote_folder)
+ self.logger.logger.debug(
+ 'Exists or create mkcol : %s', ownnotes_remote_folder)
return is_connected
def exists_or_create(self, relpath):
- response = self.wc.propfind(uri=self.get_abspath(''),
+ self.logger.logger.debug(
+ 'Exists or create %s' % self.get_abspath(relpath))
+ response = self.wc.propfind(uri=self.get_abspath('', asFolder=True),
names=True,
depth=1)
@@ -196,20 +206,32 @@ def exists_or_create(self, relpath):
return False
else:
for res in response:
- if (res.href == self.get_abspath(relpath)):
+ self.logger.logger.debug('Exists or create %s : %s',
+ self.get_abspath(relpath),
+ res.href.rstrip('/'))
+ if ((res.href == self.get_abspath(relpath))
+ or (res.href.rstrip('/')
+ == self.get_abspath(relpath))):
return True
- self.wc.mkcol(self.get_abspath(relpath))
+ with self.locktoken:
+ self.logger.logger.debug(
+ 'Exists or create mkcol : %s',
+ self.get_abspath(relpath, asFolder=True))
+ self.wc.mkcol(self.get_abspath(relpath, asFolder=True))
def upload(self, relpath, fh):
- with self.locktoken(False):
+ with self.locktoken:
+ self.logger.logger.debug('Put : %s', self.get_abspath(relpath))
self.wc.put(self.get_abspath(relpath), fh)
def download(self, relpath, fh):
+ self.logger.logger.debug('Get : %s', self.get_abspath(relpath))
fh.write(self.wc.get(self.get_abspath(relpath)).content)
def rm(self, relpath):
with self.locktoken:
+ self.logger.logger.debug('Delete : %s', self.get_abspath(relpath))
self.wc.delete(self.get_abspath(relpath))
def get_abspath(self, relpath, asFolder=False):
@@ -244,49 +266,66 @@ def get_mtime(self, relpath):
return round(time.mktime(rfc822.parsedate(
res.get('getlastmodified').text)))
- def get_files_index(self,):
+ def get_files_index(self, path=''):
index = {}
- abspath = self.get_abspath('', asFolder=True)
-
+ abspath = self.get_abspath(path, asFolder=True)
response = self.wc.propfind(uri=abspath,
names=True,
- depth='infinity')
-
- print(response.real)
-
- if response.real != 207:
- print((response.real, dir(response.href)))
- raise NetworkError('Can\'t list file on webdav host')
+ depth='1')
+ # We can t use infinite depth some owncloud version
+ # didn t support it
- else:
+ if response.real == 207:
for res in response:
- print((requests.utils.unquote(res.href)))
if len(res.get('resourcetype').getchildren()) == 0:
- index[requests.utils.unquote(self.get_relpath(res.href))] = \
- round(time.mktime(rfc822.parsedate(
+ index[requests.utils.unquote(self.get_relpath(res.href))] \
+ = round(time.mktime(rfc822.parsedate(
res.get('getlastmodified').text)))
+ else:
+ # Workarround for infinite depth
+ if res.href != abspath:
+ index.update(
+ self.get_files_index(
+ path=self.get_relpath(res.href)))
+
+ elif response.real == 200:
+ raise NetworkError('Wrong answer from server')
+
+ else:
+ raise NetworkError('Can\'t list file on webdav host')
return index
def move(self, srcrelpath, dstrelpath):
'''Move/Rename a note on webdav'''
with self.locktoken:
+ self.logger.logger.debug('Move : %s -> %s',
+ self.get_abspath(srcrelpath),
+ self.get_abspath(srcrelpath))
self.wc.move(self.get_abspath(srcrelpath),
self.get_abspath(dstrelpath),
depth='infinity',
overwrite=True)
def lock(self, relpath=''):
- abspath = self.get_abspath(relpath)
+ '''ownCloud no longer supports WebDAV file LOCKs, so just set up an
+ empty interface'''
+ self.locktoken = fakelock()
+ return
+ # The original code to execute, if locking were implemented, follows
+ abspath = self.get_abspath(relpath, asFolder=True)
if relpath:
self.locktoken = self.wc.lock(uri=abspath, timeout=60)
else:
self.locktoken = self.wc.lock(uri=abspath,
- depth='infinity',
+ depth='Infinity',
timeout=300)
def unlock(self, relpath=None):
+ '''ownCloud no longer supports WebDAV file LOCKs, so we do nothing'''
+ return
+ # The original code to execute, if locking were implemented, follows
if self.locktoken:
self.wc.unlock(uri_or_lock=self.locktoken)
@@ -299,13 +338,17 @@ def __init__(self,):
self._running = False
self._lock = None
+ settings = Settings()
+ self.logger = logger.Logger(
+ debug=settings.get('WebDav', 'debug'))
+
# TODO
- if Settings().get('WebDav', 'startupsync') is True:
- self.launch()
+ # Duplicate, launched in qml
+ #if settings.get('WebDav', 'startupsync') is True:
+ # self.launch()
def launch(self):
''' Sync the notes in a thread'''
- print('Sync Launched')
if not self._get_running():
self._set_running(True)
self.thread = threading.Thread(target=self.sync)
@@ -314,17 +357,18 @@ def launch(self):
else:
return True
- def push_note(self, path):
+ def launch_push_note(self, path):
self.thread = threading.Thread(target=self._wpushNote, args=[path, ])
self.thread.start()
+ return True
def sync(self):
- wdc = WebdavClient()
+ wdc = WebdavClient(self.logger)
try:
wdc.connect()
- time_delta = wdc.timedelta
+ time_delta = wdc.time_delta
- ldc = localClient()
+ ldc = localClient(self.logger)
# Get remote filenames and timestamps
remote_filenames = wdc.get_files_index()
@@ -332,16 +376,11 @@ def sync(self):
# Get local filenames and timestamps
local_filenames = ldc.get_files_index()
- print(('Remote', remote_filenames))
- print(('Local', local_filenames))
-
wdc.lock()
previous_remote_index, \
previous_local_index = self._get_sync_index()
- print((previous_remote_index, previous_local_index))
-
# Delete remote file deleted
for filename in set(previous_remote_index) \
- set(remote_filenames):
@@ -400,8 +439,9 @@ def sync(self):
# Avoid false detect
if abs(local2utc(remote_filenames[filename]
- - time_delta) - local_filenames[filename]) == 0:
- print(('Ignored %s' % filename))
+ - time_delta)
+ - local_filenames[filename]) == 0:
+ pass
elif local2utc(remote_filenames[filename]
- time_delta) \
> local_filenames[filename]:
@@ -411,41 +451,41 @@ def sync(self):
< local_filenames[filename]:
self._conflictServer(wdc, ldc, filename)
else:
- print(('Ignored %s' % filename))
+ pass
# Build and write index
self._write_index(wdc, ldc)
# Unlock the collection
wdc.unlock()
- print('Sync end')
+
except requests.exceptions.SSLError as err:
raise SSLError('SSL Certificate is not valid, or is self signed')
except Exception as err:
import traceback
- traceback.print_exc()
+ self.logger.logger.error(traceback.format_exc())
wdc.unlock()
raise err
self._set_running(False)
return True
- def note_push(self, relpath):
+ def push_note(self, relpath):
''' Given full path of a textual note file, push that file to the
remote server '''
self._set_running(True)
- ldc = localClient()
+ ldc = localClient(self.logger)
# Create Connection
- wdc = WebdavClient()
+ wdc = WebdavClient(self.logger)
wdc.connect()
wdc.lock(relpath)
# Get mtime
- remote_mtime = self._get_mtime(relpath)
+ remote_mtime = wdc.get_mtime(relpath)
local_mtime = ldc.get_mtime(ldc.get_abspath(relpath))
if local_mtime >= local2utc(remote_mtime - wdc.time_delta):
@@ -459,7 +499,6 @@ def note_push(self, relpath):
def _conflictServer(self, wdc, ldc, path):
'''Priority to local'''
- print(('conflictServer: %s' % path))
conflict_path = os.path.splitext(path)[0] + '.Conflict.txt' # FIXME
@@ -478,7 +517,7 @@ def _conflictServer(self, wdc, ldc, path):
def _conflictLocal(self, wdc, ldc, relpath):
'''Priority to server'''
- print(('conflictLocal: %s', relpath))
+
conflict_path = os.path.splitext(relpath)[0] + '.Conflict.txt' # FIXME
ldc.rename(relpath, conflict_path)
@@ -493,6 +532,19 @@ def _conflictLocal(self, wdc, ldc, relpath):
else:
self._upload(wdc, ldc, conflict_path)
+ def get_last_sync_datetime(self):
+ try:
+ # TODO Use XDG Env
+ return time.strftime('%x %X',
+ time.localtime(
+ os.path.getmtime(
+ os.path.join(
+ os.path.expanduser(
+ '~/.ownnotes/'),
+ '.index.sync'))))
+ except:
+ return ''
+
def _get_sync_index(self):
index = {'remote': [], 'local': []}
try:
@@ -503,7 +555,7 @@ def _get_sync_index(self):
'r') as fh:
index = json.load(fh)
except (IOError, TypeError, ValueError) as err:
- print((
+ self.logger.logger.info((
'First sync detected or error: %s'
% str(err)))
if type(index) == list:
@@ -523,19 +575,7 @@ def _write_index(self, wdc, ldc):
merge_dir = ldc.get_abspath('.merge.sync/')
if os.path.exists(merge_dir):
shutil.rmtree(merge_dir)
-
- # os.makedirs(merge_dir)
- # for filename in glob.glob(os.path.join(
- # self._localDataFolder, '*.txt')):
- # try:
- # if os.path.isfile(filename):
- # shutil.copy(filename,
- # os.path.join(
- # merge_dir,
- # self.localBasename(filename)))
- # except IOError as err:
- # print(err, 'filename:', filename, ' merge_dir:',
- # merge_dir)
+ print('index written')
def _rm_remote_index(self,):
'''Delete the remote index stored locally'''
@@ -549,20 +589,19 @@ def _rm_remote_index(self,):
'.index.sync'), 'w') as fh:
json.dump(({}, index[1]), fh)
except:
- print('No remote index stored locally')
+ self.logger.logger.info('No remote index stored locally')
def _upload(self, wdc, ldc, local_relpath, remote_relpath=None):
if not remote_relpath:
remote_relpath = local_relpath
- rdirname, rfilename = (os.path.dirname(remote_relpath),
- os.path.basename(remote_relpath))
+ rdirname = os.path.dirname(remote_relpath)
wdc.exists_or_create(rdirname)
mtime = None
with open(ldc.get_abspath(local_relpath), 'rb') as fh:
wdc.upload(remote_relpath, fh)
- mtime = local2utc(wdc.get_mtime(remote_relpath)) - wdc.timedelta
+ mtime = local2utc(wdc.get_mtime(remote_relpath)) - wdc.time_delta
if mtime:
ldc.set_mtime(local_relpath, mtime)
@@ -570,8 +609,6 @@ def _upload(self, wdc, ldc, local_relpath, remote_relpath=None):
def _download(self, wdc, ldc, remote_relpath, local_relpath=None):
if not local_relpath:
local_relpath = remote_relpath
- rdirname, rfilename = (os.path.dirname(remote_relpath),
- os.path.basename(remote_relpath))
if not os.path.exists(os.path.dirname(ldc.get_abspath(local_relpath))):
os.makedirs(os.path.dirname(ldc.get_abspath(local_relpath)))
@@ -579,20 +616,11 @@ def _download(self, wdc, ldc, remote_relpath, local_relpath=None):
mtime = None
with open(ldc.get_abspath(local_relpath), 'wb') as fh:
wdc.download(remote_relpath, fh)
- mtime = wdc.get_mtime(remote_relpath) - wdc.timedelta
+ mtime = wdc.get_mtime(remote_relpath) - wdc.time_delta
if mtime:
ldc.set_mtime(local_relpath, mtime)
- '''def _get_mtime(self, webdavConnection, remote_filename):
- rdirname, rfilename = (os.path.dirname(remote_filename),
- os.path.basename(remote_filename))
- webdavConnection.path = webdavPathJoin(self._get_notes_path(),
- rdirname, rfilename)
- return time.mktime(webdavConnection
- .readStandardProperties()
- .getLastModified())'''
-
def _remote_rm(self, wdc, relpath):
wdc.rm(relpath)
diff --git a/Sailfish/ownNotes/python/tinydav/__init__.py b/Sailfish/ownNotes/python/tinydav/__init__.py
index 9a0bc34..f5eba84 100755
--- a/Sailfish/ownNotes/python/tinydav/__init__.py
+++ b/Sailfish/ownNotes/python/tinydav/__init__.py
@@ -28,6 +28,7 @@
from email.header import Header
from functools import wraps, partial
import requests
+import time
if PYTHON2:
from http.client import MULTI_STATUS, OK, CONFLICT, NO_CONTENT, UNAUTHORIZED
@@ -678,9 +679,6 @@ def __init__(self, host, port=80, protocol=None, strict=False,
8080 -> http
8081 -> http
Default port is 'http'.
- strict -- When True, raise BadStatusLine if the status line can't be
- parsed as a valid HTTP/1.0 or 1.1 status line (see Python
- doc for httplib).
timeout -- Operations will timeout after that many seconds. Else the
global default timeout setting is used (see Python doc for
httplib). This argument is available since Python 2.6. It
@@ -700,7 +698,6 @@ def __init__(self, host, port=80, protocol=None, strict=False,
self.protocol = PROTOCOL.get(port, "http")
else:
self.protocol = protocol
- self.strict = strict
self.timeout = timeout
self.source_address = source_address
if PYTHON2:
@@ -716,7 +713,7 @@ def __init__(self, host, port=80, protocol=None, strict=False,
def _getconnection(self):
"""Return HTTP(S)Connection object depending on set protocol."""
args = (self.host, self.port,)
- kwargs = dict(strict=self.strict)
+ kwargs = dict()
if PYTHON2_6:
kwargs["timeout"] = self.timeout
if PYTHON2_7:
@@ -761,7 +758,10 @@ def _request(self, method, uri, content=None, headers=None):
data=content,
headers=headers,
cookies=self.cookie,
- verify=(not self.nosslcheck)))
+ verify=(not self.nosslcheck),
+ timeout=60))
+ with open('/tmp/ownnotes_'+str(time.time()), 'w') as fh:
+ fh.write('uri:%s\nmethod:%s\ncontent:%s\nresult:%s' % (uri, method, content, response.content))
if 400 <= response < 500:
response = HTTPUserError(response)
diff --git a/Sailfish/ownNotes/python/tinydav/__init__.py.bak b/Sailfish/ownNotes/python/tinydav/__init__.py.bak
deleted file mode 100755
index 89c4a49..0000000
--- a/Sailfish/ownNotes/python/tinydav/__init__.py.bak
+++ /dev/null
@@ -1,1424 +0,0 @@
-# The tinydav WebDAV client.
-# Copyright (C) 2009 Manuel Hermann
-#
-# This file is part of tinydav.
-#
-# tinydav is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see .
-"""The tinydav WebDAV client."""
-from __future__ import with_statement
-import sys
-
-PYTHON2_6 = (sys.version_info >= (2, 6))
-PYTHON2_7 = (sys.version_info >= (2, 7))
-PYTHON2 = ((2, 5) <= sys.version_info <= (3, 0))
-PYTHON3 = (sys.version_info >= (3, 0))
-
-from contextlib import closing
-from email.header import Header
-from functools import wraps, partial
-import requests
-
-if PYTHON2:
- from httplib import MULTI_STATUS, OK, CONFLICT, NO_CONTENT, UNAUTHORIZED
- from urllib import quote as urllib_quote
- from urllib import urlencode as urllib_urlencode
- from StringIO import StringIO
- import httplib
-else:
- from http.client import MULTI_STATUS, OK, CONFLICT, NO_CONTENT
- from http.client import UNAUTHORIZED
- from io import BytesIO
- from io import StringIO
- from urllib.parse import quote as urllib_quote
- from urllib.parse import urlencode as urllib_urlencode
- import base64
- import http.client as httplib
-
-from xml.etree.ElementTree import ElementTree, Element, SubElement, tostring
-
-if PYTHON2_7 or PYTHON3:
- from xml.etree.ElementTree import ParseError
-else:
- from xml.parsers.expat import ExpatError as ParseError
-
-import hashlib
-
-if PYTHON2:
- import urlparse
-else:
- import urllib.parse as urlparse
-
-from tinydav import creator, util
-from tinydav.exception import HTTPError, HTTPUserError, HTTPServerError
-
-__author__ = "Manuel Hermann "
-__license__ = "LGPL"
-__version__ = "0.7.3"
-
-__all__ = (
- "HTTPError", "HTTPUserError", "HTTPServerError",
- "HTTPClient", "WebDAVClient",
-)
-
-# RFC 2518, 9.8 Timeout Request Header
-# The timeout value for TimeType "Second" MUST NOT be greater than 2^32-1.
-MAX_TIMEOUT = 2**32-1
-
-ACTIVELOCK = "./{DAV:}lockdiscovery/{DAV:}activelock"
-
-# map with default ports mapped to http protocol
-PROTOCOL = {
- 80: "http",
- 443: "https",
- 8080: "http",
- 8081: "http",
-}
-
-SCHEME_MAP = {
- "webdav": ("http", 80),
- "webdavs": ("https", 443),
- "http": ("http", 80),
- "https": ("https", 443),
-}
-
-
-default_header_encoding = "utf-8"
-separate_query_sequences = True
-
-
-# Responses
-class HTTPResponse(int):
- """Result from HTTP request.
-
- An HTTPResponse object is a subclass of int. The int value of such an
- object is the HTTP status number from the response.
-
- This object has the following attributes:
-
- response -- The original httplib.HTTPResponse object.
- headers -- A dictionary with the received headers.
- content -- The content of the response as string.
- statusline -- The received HTTP status line. E.g. "HTTP/1.1 200 OK".
-
- """
-
- def __new__(cls, response):
- """Construct HTTPResponse.
-
- response -- The original httplib.HTTPResponse object.
-
- """
- return int.__new__(cls, response.status_code)
-
- def __init__(self, response):
- """Initialize the HTTPResponse.
-
- response -- The original httplib.HTTPResponse object.
-
- """
- self.response = response
- self.headers = dict(response.headers)
- self.content = response.content
- version = "HTTP/%s.%s" % tuple(str(response.raw.version))
- self.statusline = "%s %d %s"\
- % (version, response.status_code, response.reason)
- if self == UNAUTHORIZED:
- self._setauth()
-
- def __repr__(self):
- """Return representation."""
- if PYTHON2:
- return "<%s: %d>" % (self.__class__.__name__, self)
- else:
- return "<{0}: {1}>".format(self.__class__.__name__, self)
-
- def __str__(self):
- """Return string representation."""
- return self.statusline
-
- def _setauth(self):
- value = self.headers.get("www-authenticate", "")
- auth = util.parse_authenticate(value)
- for attrname in ("schema", "realm", "domain", "nonce", "opaque"):
- setattr(self, attrname, auth.get(attrname))
- stale = auth.get("stale")
- if stale is None:
- stale = "false"
- self.stale = (stale.lower() == "true")
- algorithm = auth.get("algorithm")
- if algorithm is None:
- algorithm = "MD5"
- self.algorithm = getattr(hashlib, algorithm.lower())
-
-
-class WebDAVResponse(HTTPResponse):
- """Result from WebDAV request.
-
- A WebDAVResponse object is a subclass of int. The int value of such an
- object is the HTTP status number from the response.
-
- This object has the following attributes:
-
- response -- The original httplib.HTTPResponse object.
- headers -- A dictionary with the received headers.
- content -- The content of the response as string.
- statusline -- The received HTTP status line. E.g. "HTTP/1.1 200 OK".
- is_multistatus -- True, if the response's content is a multi-status
- response.
-
- You can iterate over a WebDAVResponse object. If the received data was
- a multi-status response, the iterator will yield a MultiStatusResponse
- object per result. If it was no multi-status response, the iterator will
- just yield this WebDAVResponse object.
-
- The length of a WebDAVResponse object is 1, except for multi-status
- responses. The length will then be the number of results in the
- multi-status.
-
- """
- def __init__(self, response):
- """Initialize the WebDAVResponse.
-
- response -- The original httplib.HTTPResponse object.
-
- """
- super(WebDAVResponse, self).__init__(response)
- self._etree = ElementTree()
- # on XML parsing error set this to the raised exception
- self.parse_error = None
- self.is_multistatus = False
- if (self == MULTI_STATUS):
- self._set_multistatus()
-
- def __len__(self):
- """Return the number of responses in a multistatus response.
-
- When the response was no multistatus the return value is 1.
-
- """
- if self.is_multistatus:
- # RFC 2518, 12.9 multistatus XML Element
- #
- return len(self._etree.findall("./{DAV:}response"))
- return 1
-
- def __iter__(self):
- """Iterator over the response.
-
- Yield MultiStatusResponse instances for each response in a 207
- response.
- Yield self otherwise.
-
- """
- if self.is_multistatus:
- # RFC 2518, 12.9 multistatus XML Element
- #
- for response in self._etree.findall("./{DAV:}response"):
- yield MultiStatusResponse(response)
- else:
- yield self
-
- def _parse_xml_content(self):
- """Parse the XML content.
-
- If the response content cannot be parsed as XML content,
- will be taken as content instead.
-
- """
- try:
- if PYTHON2:
- parse_me = StringIO(self.content)
- else:
- parse_me = BytesIO(self.content)
- self._etree.parse(parse_me)
- except ParseError:
- # get the exception object this way to be compatible with Python
- # versions 2.5 up to 3.x
- self.parse_error = sys.exc_info()[1]
- # don't fail on further processing
- self._etree.parse(StringIO(""))
-
- def _set_multistatus(self):
- """Set this response to a multistatus response."""
- self.is_multistatus = True
- self._parse_xml_content()
-
-
-class WebDAVLockResponse(WebDAVResponse):
- """Result from WebDAV LOCK request.
-
- A WebDAVLockResponse object is a subclass of WebDAVResponse which is a
- subclass of int. The int value of such an object is the HTTP status number
- from the response.
-
- This object has the following attributes:
-
- response -- The original httplib.HTTPResponse object.
- headers -- A dictionary with the received headers.
- content -- The content of the response as string.
- statusline -- The received HTTP status line. E.g. "HTTP/1.1 200 OK".
- is_multistatus -- True, if the response's content is a multi-status
- response.
- lockscope -- Specifies whether a lock is an exclusive lock, or a
- shared lock.
- locktype -- Specifies the access type of a lock (which is always write).
- depth -- The value of the Depth header.
- owner -- The principal taking out this lock.
- timeout -- The timeout associated with this lock
- locktoken -- The lock token associated with this lock.
-
- You can iterate over a WebDAVLockResponse object. If the received data was
- a multi-status response, the iterator will yield a MultiStatusResponse
- object per result. If it was no multi-status response, the iterator will
- just yield this WebDAVLockResponse object.
-
- The length of a WebDAVLockResponse object is 1, except for multi-status
- responses. The length will then be the number of results in the
- multi-status.
-
- You can use this object to make conditional requests. For this, the context
- manager protocol is implemented:
-
- >>> lock = dav.lock("somewhere")
- >>> with lock:
- >>> dav.put("somwhere", )
-
- The above example will make a tagged PUT request. For untagged requests do:
-
- >>> lock = dav.lock("somewhere")
- >>> with lock(False):
- >>> dav.put("somwhere", )
-
- """
- def __new__(cls, client, uri, response):
- """Construct WebDAVLockResponse.
-
- client -- HTTPClient instance or one of its subclasses.
- uri -- The called uri.
- response --The original httplib.HTTPResponse object.
-
- """
- return WebDAVResponse.__new__(cls, response)
-
- def __init__(self, client, uri, response):
- """Initialize the WebDAVLockResponse.
-
- client -- HTTPClient instance or one of its subclasses.
- uri -- The called uri.
- response -- The original httplib.HTTPResponse object.
-
- """
- super(WebDAVLockResponse, self).__init__(response)
- self._client = None
- self._uri = None
- self._locktype = None
- self._lockscope = None
- self._depth = None
- self._owner = None
- self._timeout = None
- self._locktokens = None
- self._previous_if = None
- self._tagged = True
- self._tag = None
- # RFC 2518, 8.10.7 Status Codes
- # 200 (OK) - The lock request succeeded and the value of the
- # lockdiscovery property is included in the body.
- if self == OK:
- self._parse_xml_content()
- self._client = client
- self._uri = uri
- self._tag = util.make_absolute(self._client, uri)
- # RFC 2518, 8.10.4 Depth and Locking
- # If the lock cannot be granted to all resources, a 409 (Conflict)
- # status code MUST be returned with a response entity body
- # containing a multistatus XML element describing which resource(s)
- # prevented the lock from being granted.
- elif self == CONFLICT:
- self._set_multistatus()
-
- def __repr__(self):
- """Return representation."""
- return "<%s: <%s> %d>" % (self.__class__.__name__, self._tag, self)
-
- def __call__(self, tagged=True):
- """Configure this lock to use tagged header or not.
-
- tagged -- True, if the If header should contain a tagged list.
- False, if the If header should contain a no-tag-list.
- Default is True.
-
- """
- self._tagged = tagged
- return self
-
- def __enter__(self):
- """Use the lock on requests on the returned prepare WebDAVClient."""
- if self.locktokens:
- # RFC 2518, 9.4 If Header
- # If = "If" ":" ( 1*No-tag-list | 1*Tagged-list)
- # No-tag-list = List
- # Tagged-list = Resource 1*List
- # Resource = Coded-URL
- # List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")"
- # State-token = Coded-URL
- # Coded-URL = "<" absoluteURI ">"
- self._previous_if = self._client.headers.get("If")
- tokens = "".join("<%s>" % token for token in self.locktokens)
- if self._tagged:
- if_value = "<%s> (%s)" % (self._tag, tokens)
- else:
- if_value = "(%s)" % tokens
- self._tagged = True
- self._client.headers["If"] = if_value
- return self._client
-
- def __exit__(self, exc, exctype, exctb):
- """Remove If statement in WebDAVClient."""
- if "If" in self._client.headers:
- if self._previous_if is not None:
- self._client.headers["If"] = self._previous_if
- self._previous_if = None
- else:
- del self._client.headers["If"]
-
- @property
- def lockscope(self):
- """Return the lockscope as ElementTree element."""
- if self._lockscope is None:
- # RFC 2518, 12.7 lockscope XML Element
- #
- # RFC 2518, 12.7.1 exclusive XML Element
- #
- # RFC 2518, 12.7.2 shared XML Element
- #
- scope = ACTIVELOCK + "/{DAV:}lockscope/*"
- self._lockscope = self._etree.find(scope)
- return self._lockscope
-
- @property
- def locktype(self):
- """Return the type of this lock."""
- if self._locktype is None:
- # RFC 2518, 12.8 locktype XML Element
- #
- locktype = ACTIVELOCK + "/{DAV:}locktype/*"
- self._locktype = self._etree.find(locktype)
- return self._locktype
-
- @property
- def depth(self):
- """Return the applied depth."""
- if self._depth is None:
- # RFC 2518, 12.1.1 depth XML Element
- #
- depth = ACTIVELOCK + "/{DAV:}depth"
- self._depth = self._etree.findtext(depth)
- return self._depth
-
- @property
- def owner(self):
- """Return the owner ElementTree element or None, if there's no owner."""
- if self._owner is None:
- # RFC 2518, 12.10 owner XML Element
- #
- owner = ACTIVELOCK + "/{DAV:}owner"
- self._owner = self._etree.find(owner)
- return self._owner
-
- @property
- def timeout(self):
- """Return the timeout of this lock or None, if not available."""
- if self._timeout is None:
- # RFC 2518, 12.1.3 timeout XML Element
- #
- timeout = ACTIVELOCK + "/{DAV:}timeout"
- self._timeout = self._etree.findtext(timeout).strip()
- return self._timeout
-
- @property
- def locktokens(self):
- """Return the locktokens for this lock."""
- if self._locktokens is None:
- # RFC 2518, 12.1.2 locktoken XML Element
- #
- token = ACTIVELOCK + "/{DAV:}locktoken/{DAV:}href"
- self._locktokens = [t.text.strip()
- for t in self._etree.findall(token)]
- return self._locktokens
-
-
-class MultiStatusResponse(int):
- """Wrapper for multistatus responses.
-
- A MultiStatusResponse object is a subclass of int. The int value of such an
- object is the HTTP status number from the response.
-
- Furthermore this object implements the dictionary interface. Through it
- you can access all properties that the resource has.
-
- This object has the following attributes:
-
- statusline -- The received HTTP status line. E.g. "HTTP/1.1 200 OK".
- href -- The HREF of the resource this status is for.
- namespaces -- A frozenset with all the XML namespaces that the underlying
- XML structure had.
-
- """
- def __new__(cls, response):
- """Create instance with status code as int value."""
- # RFC 2518, 12.9.1 response XML Element
- #
- statusline = response.findtext("{DAV:}propstat/{DAV:}status")
- status = int(statusline.split()[1])
- return int.__new__(cls, status)
-
- def __init__(self, response):
- """Initialize the MultiStatusResponse.
-
- response -- ElementTree element: response-tag.
-
- """
- self.response = response
- self._href = None
- self._statusline = None
- self._namespaces = None
-
- def __repr__(self):
- """Return representation string."""
- return "<%s: %d>" % (self.__class__.__name__, self)
-
- def __getitem__(self, name):
- """Return requested property as ElementTree element.
-
- name -- Name of the property with namespace. No namespace needed for
- DAV properties.
-
- """
- # check, whether it's a default DAV property name
- if not name.startswith("{"):
- name = "{DAV:}%s" % name
- # RFC 2518, 12.9.1.1 propstat XML Element
- #
- prop = self.response.find("{DAV:}propstat/{DAV:}prop/%s" % name)
- if prop is None:
- raise KeyError(name)
- return prop
-
- if PYTHON2:
- def __iter__(self):
- """Iterator over propertynames with their namespaces."""
- return self.iterkeys()
- else:
- def __iter__(self):
- """Iterator over propertynames with their namespaces."""
- return self.keys()
-
- if PYTHON2:
- def keys(self):
- """Return list of propertynames with their namespaces.
-
- No namespaces for DAV properties.
-
- """
- return list(self.iterkeys())
-
- def iterkeys(self, cut_dav_ns=True):
- """Iterate over propertynames with their namespaces.
-
- cut_dav_ns -- No namespaces for DAV properties when this is True.
-
- """
- for (tagname, value) in self.iteritems(cut_dav_ns):
- yield tagname
- else:
- def keys(self, cut_dav_ns=True):
- """Iterate over propertynames with their namespaces.
-
- cut_dav_ns -- No namespaces for DAV properties when this is True.
-
- """
- for (tagname, value) in self.items(cut_dav_ns):
- yield tagname
-
- def items(self):
- """Return list of 2-tuples with propertyname and ElementTree element."""
- return list(self.iteritems())
-
- def iteritems(self, cut_dav_ns=True):
- """Iterate list of 2-tuples with propertyname and ElementTree element.
-
- cut_dav_ns -- No namespaces for DAV properties when this is True.
-
- """
- # RFC 2518, 12.11 prop XML element
- #
- props = self.response.findall("{DAV:}propstat/{DAV:}prop/*")
- for prop in props:
- tagname = prop.tag
- if cut_dav_ns and tagname.startswith("{DAV:}"):
- tagname = tagname[6:]
- yield (tagname, prop)
-
- if PYTHON3:
- items = iteritems
- del iteritems
-
- def get(self, key, default=None, namespace=None):
- """Return value for requested property.
-
- key -- Property name with namespace. Namespace may be omitted, when
- namespace-argument is given, or Namespace is DAV:
- default -- Return this value when key does not exist.
- namespace -- The namespace in which the property lives in. Must be
- given, when the key value has no namespace defined and
- the namespace ist not DAV:.
-
- """
- if namespace:
- key = "{%s}%s" % (namespace, key)
- try:
- return self[key]
- except KeyError:
- return default
-
- @property
- def statusline(self):
- """Return the status line for this response."""
- if self._statusline is None:
- # RFC 2518, 12.9.1.2 status XML Element
- #
- statustag = self.response.findtext("{DAV:}propstat/{DAV:}status")
- self._statusline = statustag
- return self._statusline
-
- @property
- def href(self):
- """Return the href for this response."""
- if self._href is None:
- # RFC 2518, 12.3 href XML Element
- #
- self._href = self.response.findtext("{DAV:}href")
- return self._href
-
- if PYTHON2:
- @property
- def namespaces(self):
- """Return frozenset of namespaces."""
- if self._namespaces is None:
- self._namespaces = frozenset(util.extract_namespace(key)
- for key in self.iterkeys(False)
- if util.extract_namespace(key))
- return self._namespaces
- else:
- @property
- def namespaces(self):
- """Return frozenset of namespaces."""
- if self._namespaces is None:
- self._namespaces = frozenset(util.extract_namespace(key)
- for key in self.keys(False)
- if util.extract_namespace(key))
- return self._namespaces
-
-
-# Clients
-class HTTPClient(object):
- """Mini HTTP client.
-
- This object has the following attributes:
-
- host -- Given host on initialization.
- port -- Given port on initialization.
- protocol -- Used protocol. Either chosen by the port number or taken
- from given value in initialization.
- headers -- Dictionary with headers to send with every request.
- cookie -- If set with setcookie: the given object.
- locks -- Mapping with locks.
-
- """
-
- ResponseType = HTTPResponse
-
- @classmethod
- def fromurl(cls, uri, **kwargs):
- """Construct HTTPClient instance from given uri."""
- parsed = urlparse.urlparse(uri)
-
- (protocol, port) = SCHEME_MAP[parsed.scheme]
- if parsed.port:
- port = parsed.port
-
- self = cls(parsed.hostname, port=port, protocol=protocol, **kwargs)
- if parsed.username:
- self.setbasicauth(parsed.username, parsed.password)
- return self
-
- def __init__(self, host, port=80, protocol=None, strict=False,
- timeout=None, source_address=None, nosslcheck=False):
- """Initialize the WebDAV client.
-
- host -- WebDAV server host.
- port -- WebDAV server port.
- protocol -- Override protocol name. Is either 'http' or 'https'. If
- not given, the protocol will be chosen by the port number
- automatically:
- 80 -> http
- 443 -> https
- 8080 -> http
- 8081 -> http
- Default port is 'http'.
- strict -- When True, raise BadStatusLine if the status line can't be
- parsed as a valid HTTP/1.0 or 1.1 status line (see Python
- doc for httplib).
- timeout -- Operations will timeout after that many seconds. Else the
- global default timeout setting is used (see Python doc for
- httplib). This argument is available since Python 2.6. It
- won't have any effect in previous version.
- source_address -- A tuple of (host, port) to use as the source address
- the HTTP connection is made from (see Python doc for
- httplib). This argument is available since
- Python 2.7. It won't have any effect in previous
- versions.
-
- """
- assert isinstance(port, int)
- assert protocol in (None, "http", "https")
- self.host = host
- self.port = port
- if protocol is None:
- self.protocol = PROTOCOL.get(port, "http")
- else:
- self.protocol = protocol
- self.strict = strict
- self.timeout = timeout
- self.source_address = source_address
- if PYTHON2:
- self.key_file = None
- self.cert_file = None
- else:
- self.context = None
- self.headers = dict()
- self.cookie = None
- self._do_digest_auth = False
- self.nosslcheck = nosslcheck
-
- def _getconnection(self):
- """Return HTTP(S)Connection object depending on set protocol."""
- args = (self.host, self.port,)
- kwargs = dict(strict=self.strict)
- if PYTHON2_6:
- kwargs["timeout"] = self.timeout
- if PYTHON2_7:
- kwargs["source_address"] = self.source_address
- if self.protocol == "http":
- return httplib.HTTPConnection(*args, **kwargs)
- # setup HTTPS
- if PYTHON2:
- kwargs["key_file"] = self.key_file
- kwargs["cert_file"] = self.cert_file
- else:
- kwargs["context"] = self.context
- return httplib.HTTPSConnection(*args, **kwargs)
-
- def _request(self, method, uri, content=None, headers=None):
- """Make request and return response.
-
- method -- Request method.
- uri -- URI the request is for.
- content -- The content of the request. May be None.
- headers -- If given, a mapping with additonal headers to send.
-
- """
-
- if not uri.startswith("/"):
- uri = "/%s" % uri
-
- headers = dict() if (headers is None) else headers
-
- # handle cookies, if necessary
- if self.cookie is not None:
- fake_request = util.FakeHTTPRequest(self, uri, headers)
- self.cookie.add_cookie_header(fake_request)
-
- con = self._getconnection()
- with closing(con):
- #con.request(method, uri, content, headers)
- #response = self.ResponseType(con.getresponse())
- response = self.ResponseType(
- requests.request(method=method,
- url=self.protocol+'://'+self.host+uri,
- data=content,
- headers=headers,
- cookies=self.cookie,
- verify=(not self.nosslcheck)))
-
- if 400 <= response < 500:
- response = HTTPUserError(response)
- elif 500 <= response < 600:
- response = HTTPServerError(response)
-
- if self.cookie is not None:
- # Get response object suitable for cookielib
- cookie_response = util.get_cookie_response(response)
- self.cookie.extract_cookies(cookie_response, fake_request)
-
- if isinstance(response, HTTPError):
- raise response
- return response
-
- def _prepare(self, uri, headers, query=None):
- """Return 2-tuple with prepared version of uri and headers.
-
- The headers will contain the authorization headers, if given.
-
- uri -- URI the request is for.
- headers -- Mapping with additional headers to send. Unicode values that
- are no ASCII will be MIME-encoded with UTF-8. Set
- tinydav.default_header_encoding to another encoding, if
- UTF-8 doesn't suit you.
- query -- Mapping with key/value-pairs to be added as query to the URI.
-
- """
- uri = urllib_quote(uri)
- # collect headers
- sendheaders = dict(self.headers)
- if headers:
- sendheaders.update(headers)
- for (key, value) in sendheaders.items():
- try:
- unicode(value).encode("ascii")
- except UnicodeError:
- value = str(Header(value, default_header_encoding))
- sendheaders[key] = value
- # construct query string
- if query:
- querystr = urllib_urlencode(query, doseq=separate_query_sequences)
- uri = "%s?%s" % (uri, querystr)
- return (uri, sendheaders)
-
- if PYTHON2:
- def setbasicauth(self, user, password):
- """Set authorization header for basic auth.
-
- user -- Username
- password -- Password for user.
-
- """
- # RFC 2068, 11.1 Basic Authentication Scheme
- # basic-credentials = "Basic" SP basic-cookie
- # basic-cookie =
- # user-pass = userid ":" password
- # userid = *
- # password = *TEXT
- userpw = "%s:%s" % (user, password)
- auth = userpw.encode("base64").rstrip()
- self.headers["Authorization"] = "Basic %s" % auth
- else:
- def setbasicauth(self, user, password,
- b64encoder=base64.standard_b64encode):
- """Set authorization header for basic auth.
-
- user -- Username as bytes string.
- password -- Password for user as bytes.
- encoder -- Base64 encoder function. Default is the standard
- encoder. Should not be changed.
-
- """
- # RFC 2068, 11.1 Basic Authentication Scheme
- # basic-credentials = "Basic" SP basic-cookie
- # basic-cookie =
- # user-pass = userid ":" password
- # userid = *
- # password = *TEXT
- userpw = user + bytes(":", "ascii") + password
- auth = b64encoder(userpw).decode("ascii")
- self.headers["Authorization"] = "Basic {0}".format(auth)
-
- def setcookie(self, cookie):
- """Set cookie class to be used in requests.
-
- cookie -- Cookie class from cookielib.
-
- """
- self.cookie = cookie
-
- if PYTHON2:
- def setssl(self, key_file=None, cert_file=None):
- """Set SSL key file and/or certificate chain file for HTTPS.
-
- Calling this method has the side effect of setting the protocol to
- https.
-
- key_file -- The name of a PEM formatted file that contains your
- private key.
- cert_file -- PEM formatted certificate chain file (see Python doc
- for httplib).
- """
- self.key_file = key_file
- self.cert_file = cert_file
- if any((key_file, cert_file)):
- self.protocol = "https"
- else:
- def setssl(self, context):
- """Set SSLContext for this connection.
-
- Calling this method has the side effect of setting the protocol to
- https.
-
- context -- ssl.SSLContext instance describing the various SSL
- options.
-
- """
- self.protocol = "https"
- self.context = context
-
- def options(self, uri, headers=None):
- """Make OPTIONS request and return status.
-
- uri -- URI of the request.
- headers -- Optional mapping with headers to send.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers)
- return self._request("OPTIONS", uri, None, headers)
-
- def get(self, uri, headers=None, query=None):
- """Make GET request and return status.
-
- uri -- URI of the request.
- headers -- Optional mapping with headers to send.
- query -- Mapping with key/value-pairs to be added as query to the URI.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers, query)
- return self._request("GET", uri, None, headers)
-
- def head(self, uri, headers=None, query=None):
- """Make HEAD request and return status.
-
- uri -- URI of the request.
- headers -- Optional mapping with headers to send.
- query -- Mapping with key/value-pairs to be added as query to the URI.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers, query)
- return self._request("HEAD", uri, None, headers)
-
- def post(self, uri, content="", headers=None, query=None,
- as_multipart=False, encoding="ascii", with_filenames=False):
- """Make POST request and return HTTPResponse.
-
- uri -- Path to post data to.
- content -- File descriptor, string or dict with content to POST. If it
- is a dict, the dict contents will be posted as content type
- application/x-www-form-urlencoded.
- headers -- If given, must be a mapping with headers to set.
- query -- Mapping with key/value-pairs to be added as query to the URI.
- as_multipart -- Send post data as multipart/form-data. content must be
- a dict, then. If content is not a dict, then this
- argument will be ignored. The values of the dict may be
- a subclass of email.mime.base.MIMEBase, which will be
- attached to the multipart as is, a 2-tuple containing
- the actual value (or file-like object) and an encoding
- for this value (or the content-type in case of a
- file-like object).
- encoding -- Send multipart content with this encoding. Default is
- ASCII.
- with_filenames -- If True, a multipart's files will be sent with the
- filename paramenter set. Default is False.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers, query)
- if isinstance(content, dict):
- if as_multipart:
- (multihead, content) = util.make_multipart(content,
- encoding,
- with_filenames)
- headers.update(multihead)
- else:
- headers["content-type"] = "application/x-www-form-urlencoded"
- content = urllib_urlencode(content)
- if hasattr(content, "read") and not PYTHON2_6:
- # python 2.5 httlib cannot handle file-like objects
- content = content.read()
- return self._request("POST", uri, content, headers)
-
- def put(self, uri, fileobject, content_type="application/octet-stream",
- headers=None):
- """Make PUT request and return status.
-
- uri -- Path for PUT.
- fileobject -- File-like object or string with content to PUT.
- content_type -- The content-type of the file. Default value is
- application/octet-stream.
- headers -- If given, must be a dict with headers to send.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers)
- headers["content-type"] = content_type
- # use 2.6 feature, if running under this version
- data = fileobject if PYTHON2_6 else fileobject.read()
- return self._request("PUT", uri, data, headers)
-
- def delete(self, uri, content="", headers=None):
- """Make DELETE request and return HTTPResponse.
-
- uri -- Path to post data to.
- content -- File descriptor or string with content.
- headers -- If given, must be a mapping with headers to set.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers)
- return self._request("DELETE", uri, content, headers)
-
- def trace(self, uri, maxforwards=None, via=None, headers=None):
- """Make TRACE request and return HTTPResponse.
-
- uri -- Path to post data to.
- maxforwards -- Number of maximum forwards. May be None.
- via -- If given, an iterable containing each station in the form
- stated in RFC2616, section 14.45.
- headers -- If given, must be a mapping with headers to set.
-
- Raise ValueError, if maxforward is not an int or convertable to
- an int.
- Raise TypeError, if via is not an iterable of string.
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers)
- if maxforwards is not None:
- # RFC 2068, 14.31 Max-Forwards
- # Max-Forwards = "Max-Forwards" ":" 1*DIGIT
- int(maxforwards)
- headers["Max-Forwards"] = str(maxforwards)
- # RFC 2068, 14.44 Via
- if via:
- headers["Via"] = ", ".join(via)
- return self._request("TRACE", uri, None, headers)
-
- def connect(self, uri, headers=None):
- """Make CONNECT request and return HTTPResponse.
-
- uri -- Path to post data to.
- headers -- If given, must be a mapping with headers to set.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers)
- return self._request("CONNECT", uri, None, headers)
-
-
-class CoreWebDAVClient(HTTPClient):
- """Basic WebDAVClient specified in RFC 2518.
-
- This object has the following attributes:
-
- host -- Given host on initialization.
- port -- Given port on initialization.
- protocol -- Used protocol. Either chosen by the port number or taken
- from given value in initialization.
- headers -- Dictionary with headers to send with every request.
- cookie -- If set with setcookie: the given object.
- locks -- Dictionary containing all active locks, mapped by tag -> Lock.
-
- """
-
- ResponseType = WebDAVResponse
-
- def __init__(self, host, port=80, protocol=None, nosslcheck=False):
- """Initialize the WebDAV client.
-
- host -- WebDAV server host.
- port -- WebDAV server port.
- protocol -- Override protocol name. Is either 'http' or 'https'. If
- not given, the protocol will be chosen by the port number
- automatically:
- 80 -> http
- 443 -> https
- 8080 -> http
- 8081 -> http
- Default port is 'http'.
-
- """
- super(CoreWebDAVClient, self).__init__(host, port, protocol,
- nosslcheck=nosslcheck)
- self.locks = dict()
-
- def _preparecopymove(self, source, destination, depth, overwrite, headers):
- """Return prepared for copy/move request version of uri and headers."""
- # RFC 2518, 8.8.3 COPY for Collections
- # A client may submit a Depth header on a COPY on a collection with a
- # value of "0" or "infinity".
- depth = util.get_depth(depth, ("0", "infinity"))
- headers = dict() if (headers is None) else headers
- (source, headers) = self._prepare(source, headers)
- # RFC 2518, 8.8 COPY Method
- # The Destination header MUST be present.
- # RFC 2518, 8.9 MOVE Method
- # Consequently, the Destination header MUST be present on all MOVE
- # methods and MUST follow all COPY requirements for the COPY part of
- # the MOVE method.
- # RFC 2518, 9.3 Destination Header
- # Destination = "Destination" ":" absoluteURI
- headers["Destination"] = util.make_absolute(self, destination)
- # RFC 2518, 8.8.3 COPY for Collections
- # A client may submit a Depth header on a COPY on a collection with
- # a value of "0" or "infinity".
- # RFC 2518, 8.9.2 MOVE for Collections
- if source.endswith("/"):
- headers["Depth"] = depth
- # RFC 2518, 8.8.4 COPY and the Overwrite Header
- # 8.9.3 MOVE and the Overwrite Header
- # If a resource exists at the destination and the Overwrite header is
- # "T" then prior to performing the copy the server MUST perform a
- # DELETE with "Depth: infinity" on the destination resource. If the
- # Overwrite header is set to "F" then the operation will fail.
- if overwrite is not None:
- headers["Overwrite"] = "T" if overwrite else "F"
- return (source, headers)
-
- def mkcol(self, uri, headers=None):
- """Make MKCOL request and return status.
-
- uri -- Path to create.
- headers -- If given, must be a dict with headers to send.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers)
- return self._request("MKCOL", uri, None, headers)
-
- def propfind(self, uri, depth=0, names=False,
- properties=None, include=None, namespaces=None,
- headers=None):
- """Make PROPFIND request and return status.
-
- uri -- Path for PROPFIND.
- depth -- Depth for PROFIND request. Default is zero.
- names -- If True, only the available namespace names are returned.
- properties -- If given, an iterable with all requested properties is
- expected.
- include -- If properties is not given, then additional properties can
- be requested with this argument.
- namespaces -- Mapping with namespaces for given properties, if needed.
- headers -- If given, must be a dict with headers to send.
-
- Raise ValueError, if illegal depth was given or if properties and
- include arguments were given.
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- namespaces = dict() if (namespaces is None) else namespaces
- # RFC 2518, 8.1 PROPFIND
- # A client may submit a Depth header with a value of "0", "1", or
- # "infinity" with a PROPFIND on a collection resource with internal
- # member URIs.
- depth = util.get_depth(depth)
- # check mutually exclusive arguments
- if all([properties, include]):
- raise ValueError("properties and include are mutually exclusive")
- (uri, headers) = self._prepare(uri, headers)
- # additional headers needed for PROPFIND
- headers["Depth"] = depth
- headers["Content-Type"] = "application/xml"
- content = creator.create_propfind(names, properties,
- include, namespaces)
- return self._request("PROPFIND", uri, content, headers)
-
- def proppatch(self, uri, setprops=None, delprops=None,
- namespaces=None, headers=None):
- """Make PROPPATCH request and return status.
-
- uri -- Path to resource to set properties.
- setprops -- Mapping with properties to set.
- delprops -- Iterable with properties to remove.
- namespaces -- dict with namespaces: name -> URI.
- headers -- If given, must be a dict with headers to send.
-
- Either setprops or delprops or both of them must be given, else
- ValueError will be risen.
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- # RFC 2518, 12.13 propertyupdate XML element
- #
- if not any((setprops, delprops)):
- raise ValueError("setprops and/or delprops must be given")
- (uri, headers) = self._prepare(uri, headers)
- # additional header for proppatch
- headers["Content-Type"] = "application/xml"
- content = creator.create_proppatch(setprops, delprops, namespaces)
- return self._request("PROPPATCH", uri, content, headers)
-
- def delete(self, uri, headers=None):
- """Make DELETE request and return WebDAVResponse.
-
- uri -- Path of resource or collection to delete.
- headers -- If given, must be a mapping with headers to set.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- headers = dict() if (headers is None) else headers
- if uri.endswith("/"):
- # RFC 2518, 8.6.2 DELETE for Collections
- # A client MUST NOT submit a Depth header with a DELETE on a
- # collection with any value but infinity.
- headers["Depth"] = "infinity"
- return super(CoreWebDAVClient, self).delete(uri, headers=headers)
-
- def copy(self, source, destination, depth="infinity",
- overwrite=None, headers=None):
- """Make COPY request and return WebDAVResponse.
-
- source -- Path of resource to copy.
- destination -- Path of destination to copy source to.
- depth -- Either 0 or "infinity". Default is the latter.
- overwrite -- If not None, then a boolean indicating whether the
- Overwrite header ist set to "T" (True) or "F" (False).
- headers -- If given, must be a mapping with headers to set.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (source, headers) = self._preparecopymove(source, destination, depth,
- overwrite, headers)
- return self._request("COPY", source, None, headers)
-
- def move(self, source, destination, depth="infinity",
- overwrite=None, headers=None):
- """Make MOVE request and return WebDAVResponse.
-
- source -- Path of resource to move.
- destination -- Path of destination to move source to.
- depth -- Either 0 or "infinity". Default is the latter.
- overwrite -- If not None, then a boolean indicating whether the
- Overwrite header ist set to "T" (True) or "F" (False).
- headers -- If given, must be a mapping with headers to set.
-
- Raise ValueError, if an illegal depth was given.
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- # RFC 2518, 8.9.2 MOVE for Collections
- # A client MUST NOT submit a Depth header on a MOVE on a collection
- # with any value but "infinity".
- if source.endswith("/") and (depth != "infinity"):
- raise ValueError("depth must be infinity when moving collections")
- (source, headers) = self._preparecopymove(source, destination, depth,
- overwrite, headers)
- return self._request("MOVE", source, None, headers)
-
- def lock(self, uri, scope="exclusive", type_="write", owner=None,
- timeout=None, depth=None, headers=None):
- """Make LOCK request and return DAVLock instance.
-
- uri -- Resource to get lock on.
- scope -- Lock scope: One of "exclusive" (default) or "shared".
- type_ -- Lock type: "write" (default) only. Any other value allowed by
- this library.
- owner -- Content of owner element. May be None, a string or an
- ElementTree element.
- timeout -- Value for the timeout header. Either "infinite" or a number
- representing the seconds (not greater than 2^32 - 1).
- headers -- If given, must be a mapping with headers to set.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- (uri, headers) = self._prepare(uri, headers)
- # RFC 2518, 9.8 Timeout Request Header
- # TimeOut = "Timeout" ":" 1#TimeType
- # TimeType = ("Second-" DAVTimeOutVal | "Infinite" | Other)
- # DAVTimeOutVal = 1*digit
- # Other = "Extend" field-value ; See section 4.2 of [RFC2068]
- if timeout is not None:
- try:
- timeout = int(timeout)
- except ValueError: # no number
- if timeout.lower() == "infinite":
- value = "Infinite"
- else:
- raise ValueError("either number of seconds or 'infinite'")
- else:
- if timeout > MAX_TIMEOUT:
- raise ValueError("timeout too big")
- value = "Second-%d" % timeout
- headers["Timeout"] = value
- # RFC 2518, 8.10.4 Depth and Locking
- # Values other than
- # 0 or infinity MUST NOT be used with the Depth header on a LOCK
- # method.
- if depth is not None:
- headers["Depth"] = util.get_depth(depth, ("0", "infinity"))
- content = creator.create_lock(scope, type_, owner)
- # set a specialized ResponseType as instance var
- self.ResponseType = partial(WebDAVLockResponse, self, uri)
- try:
- lock_response = self._request("LOCK", uri, content, headers)
- if lock_response == OK:
- self.locks[lock_response._tag] = lock_response
- return lock_response
- finally:
- # remove the formerly set ResponseType from the instance
- del self.ResponseType
-
- def unlock(self, uri_or_lock, locktoken=None, headers=None):
- """Make UNLOCK request and return WebDAVResponse.
-
- uri_or_lock -- Resource URI to unlock or WebDAVLockResponse.
- locktoken -- Use this lock token for unlocking. If not given, the
- registered locks (self.locks) will be referenced.
- headers -- If given, must be a mapping with headers to set.
-
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- if isinstance(uri_or_lock, WebDAVLockResponse):
- uri = uri_or_lock._uri
- tag = uri_or_lock._tag
- locktoken = uri_or_lock.locktokens[0]
- # uri is already prepared in WebDAVLockResponse
- (_, headers) = self._prepare("", headers)
- else:
- tag = util.make_absolute(self, uri_or_lock)
- if locktoken is None:
- try:
- lock = self.locks[tag]
- except KeyError:
- raise ValueError("no lock token")
- tag = lock._tag
- locktoken = lock.locktokens[0]
- (uri, headers) = self._prepare(uri_or_lock, headers)
- # RFC 2518, 9.5 Lock-Token Header
- # Lock-Token = "Lock-Token" ":" Coded-URL
- headers["Lock-Token"] = "<%s>" % locktoken
- response = self._request("UNLOCK", uri, None, headers)
- # RFC 2518, 8.11 UNLOCK Method
- # The 204 (No Content) status code is used instead of 200 (OK) because
- # there is no response entity body.
- if response == NO_CONTENT:
- try:
- del self.locks[tag]
- except KeyError:
- pass
- return response
-
-
-class ExtendedWebDAVClient(CoreWebDAVClient):
- """WebDAV client with versioning extensions (RFC 3253)."""
- def __report(self, uri, depth, content, headers):
- depth = util.get_depth(depth)
- (uri, headers) = self._prepare(uri, headers)
- # RFC 3253, 3.6 REPORT Method
- # The request MAY include a Depth header. If no Depth header is
- # included, Depth:0 is assumed.
- headers["Depth"] = depth
- headers["Content-Type"] = "application/xml"
- return self._request("REPORT", uri, content, headers)
-
- def version_tree_report(self, uri, depth=0, properties=None,
- elements=None, namespaces=None, headers=None):
- """Make a version-tree-REPORT request and return status.
-
- uri -- Resource or collection to get report for.
- depth -- Either 0 or 1 or "infinity". Default is zero.
- properties -- If given, an iterable with all requested properties is
- expected.
- elements -- An iterable with additional XML (ElementTree) elements to
- append to the version-tree.
- namespaces -- Mapping with namespaces for given properties, if needed.
- headers -- If given, must be a mapping with headers to set.
-
- Raise ValueError, if an illegal depth value was given.
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- args = (properties, elements, namespaces)
- content = creator.create_report_version_tree(*args)
- return self.__report(uri, depth, content, headers)
-
- # compatibility
- report = version_tree_report
-
- def expand_property_report(self, uri, depth=0, properties=None,
- elements=None, namespaces=None, headers=None):
- """Make a expand-property-REPORT request and return status.
-
- uri -- Resource or collection to get report for.
- depth -- Either 0 or 1 or "infinity". Default is zero.
- properties -- If given, an iterable with all requested properties is
- expected.
- elements -- An iterable with additional XML (ElementTree) elements to
- append to the version-tree.
- namespaces -- Mapping with namespaces for given properties, if needed.
- headers -- If given, must be a mapping with headers to set.
-
- Raise ValueError, if an illegal depth value was given.
- Raise HTTPUserError on 4xx HTTP status codes.
- Raise HTTPServerError on 5xx HTTP status codes.
-
- """
- args = (properties, elements, namespaces)
- content = creator.create_report_expand_property(*args)
- return self.__report(uri, depth, content, headers)
-
-
-class WebDAVClient(ExtendedWebDAVClient):
- """Mini WebDAV client.
-
- This object has the following attributes:
-
- host -- Given host on initialization.
- port -- Given port on initialization.
- protocol -- Used protocol. Either chosen by the port number or taken
- from given value in initialization.
- headers -- Dictionary with headers to send with every request.
- cookie -- If set with setcookie: the given object.
-
- """
diff --git a/Sailfish/ownNotes/python/tinydav/creator.py.bak b/Sailfish/ownNotes/python/tinydav/creator.py.bak
deleted file mode 100755
index d6a825b..0000000
--- a/Sailfish/ownNotes/python/tinydav/creator.py.bak
+++ /dev/null
@@ -1,224 +0,0 @@
-# Request creator function for tinydav WebDAV client.
-# Copyright (C) 2009 Manuel Hermann
-#
-# This file is part of tinydav.
-#
-# tinydav is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see .
-"""Module with helper functions that generate XML requests."""
-from collections import defaultdict
-
-try:
- from collections import Mapping
-except ImportError:
- Mapping = (dict, defaultdict)
-from xml.etree.ElementTree import Element, SubElement, tostring
-import sys
-
-PYTHON2 = ((2, 5) <= sys.version_info <= (3, 0))
-PYTHON2_6 = (sys.version_info >= (2, 6))
-STRING_TYPE = str if PYTHON2 else str
-_NS = {"xmlns": "DAV:"}
-
-
-def _addnamespaces(elem, namespaces):
- """Add namespace definitions to a given XML element.
-
- elem -- ElementTree element to add namespaces to.
- namespaces -- Mapping (prefix->namespace) with additional namespaces,
- if necessary.
-
- """
- for nsname in namespaces:
- attrname = "xmlns:%s" % nsname
- elem.attrib[attrname] = namespaces[nsname]
-
-
-def create_propfind(names=False, properties=None,
- include=None, namespaces=None):
- """Construct and return XML string for PROPFIND.
-
- names -- Boolean whether the profind is requesting property names only.
- properties -- An iterable containing property names to request. Will only
- by considered when names is False.
- include -- An Iterable containing properties that shall be returned by the
- WebDAV server in addition to the properties returned by an
- allprop request.
- namespaces -- Mapping (prefix->namespace) with additional namespaces,
- if necessary.
-
- If names is False, properties is considered False, an allprop-PROPFIND
- request is created.
-
- """
- namespaces = dict() if (namespaces is None) else namespaces
- # RFC 2518, 12.14 propfind XML Element
- #
- propfind = Element("propfind", _NS)
- _addnamespaces(propfind, namespaces)
- if names:
- # RFC 2518, 12.14.2 propname XML Element
- #
- names_element = SubElement(propfind, "propname")
- elif properties:
- # RFC 2518, 12.11 prop XML Element
- #
- prop = SubElement(propfind, "prop")
- for propname in properties:
- propelement = SubElement(prop, propname)
- else:
- # RFC 2518, 12.14.2 allprop XML Element
- #
- allprop = SubElement(propfind, "allprop")
- # draft-reschke-webdav-allprop-include-00
- #
- #
- if include:
- include_element = SubElement(propfind, "include")
- for propname in include:
- inclprop = SubElement(include_element, propname)
- return tostring(propfind, "UTF-8")
-
-
-def create_proppatch(setprops, delprops, namespaces=None):
- """Construct and return XML string for PROPPATCH.
-
- setprops -- Mapping with properties to set.
- delprops -- Iterable with element names to remove.
- namespaces -- Mapping (prefix->namespace) with additional namespaces,
- if necessary.
-
- """
- # RFC 2518, 12.13 propertyupdate XML element
- #
- propertyupdate = Element("propertyupdate", _NS)
- if namespaces:
- _addnamespaces(propertyupdate, namespaces)
- # RFC 2518, 12.13.2 set XML element
- #
- if setprops:
- set_ = SubElement(propertyupdate, "set")
- prop = SubElement(set_, "prop")
- items_iterator = iter(setprops.items()) if PYTHON2 else list(setprops.items())
- for (propname, propvalue) in items_iterator:
- prop = SubElement(prop, propname)
- prop.text = propvalue
- # RFC 2518, 12.13.1 set XML element
- #
- if delprops:
- remove = SubElement(propertyupdate, "remove")
- prop = SubElement(remove, "prop")
- for propname in delprops:
- prop = SubElement(prop, propname)
- return tostring(propertyupdate, "UTF-8")
-
-
-def create_lock(scope="exclusive", type_="write", owner=None):
- """Construct and return XML string for LOCK.
-
- scope -- One of "exclusive" or "shared".
- type_ -- Only "write" in defined in RFC.
- owner -- Optional owner information for lock. Can be any string.
-
- Raise ValueError, if illegal scope was given.
-
- """
- # RFC 2518, 12.7 lockscope XML Element
- #
- # RFC 2518, 12.7.1 exclusive XML Element
- #
- # RFC 2518, 12.7.2 shared XML Element
- #
- if scope not in ("exclusive", "shared"):
- raise ValueError("scope must be either exclusive or shared")
- # RFC 2518, 12.6 lockinfo XML Element
- #
- lockinfo = Element("lockinfo", _NS)
- # set lockscope
- lockscope = SubElement(lockinfo, "lockscope")
- scope = SubElement(lockscope, scope)
- # RFC 2518, 12.8 locktype XML Element
- #
- # RFC 2518, 12.8.1 write XML Element
- #
- locktype = SubElement(lockinfo, "locktype")
- typ_ = SubElement(locktype, type_)
- if owner is not None:
- # RFC 2518, 12.10 owner XML Element
- #
- owner_elem = SubElement(lockinfo, "owner")
- if isinstance(owner, STRING_TYPE):
- owner_elem.text = owner
- else:
- owner_elem.append(owner)
- return tostring(lockinfo)
-
-
-def create_report_version_tree(properties=None, elements=None, namespaces=None):
- """Construct and return XML for REPORT."""
- namespaces = dict() if (namespaces is None) else namespaces
- ns = {"xmlns": "DAV:"}
- # RFC 3253, 3.7 DAV:version-tree Report
- #
- # ANY value: a sequence of zero or more elements, with at most one
- # DAV:prop element.
- report = Element("version-tree", ns)
- _addnamespaces(report, namespaces)
- if properties:
- prop = SubElement(report, "prop")
- for propname in properties:
- propelement = SubElement(prop, propname)
- if elements:
- for element in elements:
- report.append(element)
- return tostring(report, "UTF-8")
-
-
-def create_report_expand_property(properties=None, elements=None,
- namespaces=None):
- """Construct and return XML for expand-property-REPORT."""
- namespaces = dict() if (namespaces is None) else namespaces
- ns = {"xmlns": "DAV:"}
- # RFC 3253, 3.8 DAV:expand-property Report
- #
- #
- #
- # name value: a property element type
- report = Element("expand-property", ns)
- _addnamespaces(report, namespaces)
-
- def attach_properties(elem, properties):
- """Attach property-Elements to given element recursivly.
-
- elem -- ElementTree.Element to attach property-Elements to.
- properties -- string, list or mapping with element-names to attach.
-
- """
- if isinstance(properties, str):
- properties = {properties: None}
- elif not isinstance(properties, Mapping):
- properties = dict.fromkeys(properties, None)
- # recursivly attach property-elements to elem
- for (propname, subprops) in list(properties.items()):
- prop = SubElement(elem, "property")
- prop.attrib["name"] = propname
- if subprops:
- attach_properties(prop, subprops)
-
- if properties:
- attach_properties(report, properties)
- if elements:
- for element in elements:
- report.append(element)
- return tostring(report, "UTF-8")
-
diff --git a/Sailfish/ownNotes/python/tinydav/util.py.bak b/Sailfish/ownNotes/python/tinydav/util.py.bak
deleted file mode 100755
index 1d8ba97..0000000
--- a/Sailfish/ownNotes/python/tinydav/util.py.bak
+++ /dev/null
@@ -1,330 +0,0 @@
-# Utility function for tinydav WebDAV client.
-# Copyright (C) 2009 Manuel Hermann
-#
-# This file is part of tinydav.
-#
-# tinydav is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see .
-"""Utility functions and classes for tinydav WebDAV client."""
-import sys
-PYTHON2 = ((2, 5) <= sys.version_info <= (3, 0))
-
-from email.encoders import encode_base64
-from email.mime.application import MIMEApplication
-from email.mime.base import MIMEBase
-from email.mime.multipart import MIMEMultipart
-from email.mime.text import MIMEText
-from os import path
-import re
-
-if PYTHON2:
- from urllib.parse import urlunsplit
-else:
- from urllib.parse import urlunsplit
-
-from tinydav.exception import HTTPError
-
-__all__ = (
- "FakeHTTPRequest", "make_absolute", "make_multipart",
- "extract_namespace", "get_depth"
-)
-
-authparser = re.compile("""
- (?PBasic|Digest)
- (
- \s+
- (?:realm="(?P[^"]*)")?
- (?:domain="(?P[^"]*)")?
- (?:nonce="(?P[^"]*)")?
- (?:opaque="(?P[^"]*)")?
- (?:stale=(?P(true|false|TRUE|FALSE)))?
- (?:algorithm="(?P\w+)")?
- )+
-""", re.VERBOSE)
-
-DEFAULT_CONTENT_TYPE = "application/octet-stream"
-
-
-class FakeHTTPRequest(object):
- """Fake HTTP request object needed for cookies.
-
- See http://docs.python.org/library/cookielib.html#cookiejar-and-filecookiejar-objects
-
- """
- def __init__(self, client, uri, headers):
- """Initialize the fake HTTP request object.
-
- client -- HTTPClient object or one of its subclasses.
- uri -- The URI to call.
- headers -- Headers dict to add cookie stuff to.
-
- """
- self._client = client
- self._uri = uri
- self._headers = headers
-
- def get_full_url(self):
- return make_absolute(self._client, self._uri)
-
- def get_host(self):
- return self._client.host
-
- def is_unverifiable(self):
- return False
-
- def get_origin_req_host(self):
- return self.get_host()
-
- def get_type(self):
- return self._client.protocol
-
- def has_header(self, name):
- return (name in self._headers)
-
- def add_unredirected_header(self, key, header):
- self._headers[key] = header
-
-
-def make_absolute(httpclient, uri):
- """Return correct absolute URI.
-
- httpclient -- HTTPClient instance with protocol, host and port attribute.
- uri -- The destination path.
-
- """
- netloc = "%s:%d" % (httpclient.host, httpclient.port)
- parts = (httpclient.protocol, netloc, uri, None, None)
- return urlunsplit(parts)
-
-
-class Multipart(object):
- def __init__(self, data, default_encoding="ascii", with_filenames=False):
- self.data = data
- self.default_encoding = default_encoding
- self.with_filenames = with_filenames
- self._mp = MIMEMultipart("form-data")
- self._files = list()
-
- def _create_non_file_parts(self):
- items_iterator = iter(self.data.items()) if PYTHON2 else list(self.data.items())
- for (key, data) in items_iterator:
- # Are there explicit encodings/content-types given?
- # Note: Cannot do a (value, encoding) = value here as fileobjects
- # then would get iterated, which is not what we want.
- if isinstance(data, tuple) and (len(data) == 2):
- (value, encoding) = data
- else:
- (value, encoding) = (data, None)
- # collect file-like objects
- if hasattr(value, "read"):
- self._files.append((key, value, encoding))
- # no file-like object
- else:
- if isinstance(value, MIMEBase):
- part = value
- else:
- encoding = encoding if encoding else default_encoding
- part = MIMEText(value, "plain", encoding)
- add_disposition(part, key)
- self._mp.attach(part)
-
- def _add_disposition(self, part, name, filename=None,
- disposition="form-data"):
- """Add a Content-Disposition header to the part.
-
- part -- Part to add header to.
- name -- Name of the part.
- filename -- Add this filename as parameter, if given.
- disposition -- Value of the content-disposition header.
-
- """
- # RFC 2388 Returning Values from Forms: multipart/form-data
- # Each part is expected to contain a content-disposition header
- # [RFC 2183] where the disposition type is "form-data", and where the
- # disposition contains an (additional) parameter of "name", where the
- # value of that parameter is the original field name in the form.
- params = dict(name=name)
- if self.with_filenames and (filename is not None):
- # RFC 2388 Returning Values from Forms: multipart/form-data
- # The original local file name may be supplied as well, either as
- # a "filename" parameter either of the "content-disposition:
- # form-data" header or, in the case of multiple files, in a
- # "content-disposition: file" header of the subpart.
- params["filename"] = path.basename(filename)
- part.add_header("Content-Disposition", disposition, **params)
-
-
-def make_multipart(content, default_encoding="ascii", with_filenames=False):
- """Return the headers and content for multipart/form-data.
-
- content -- Dict with content to POST. The dict values are expected to
- be unicode or decodable with us-ascii.
- default_encoding -- Send multipart with this encoding, if no special
- encoding was given with the content. Default is ascii.
- with_filenames -- If True, a multipart's files will be sent with the
- filename paramenter set. Default is False.
-
- """
- def add_disposition(part, name, filename=None, disposition="form-data"):
- """Add a Content-Disposition header to the part.
-
- part -- Part to add header to.
- name -- Name of the part.
- filename -- Add this filename as parameter, if given.
- disposition -- Value of the content-disposition header.
-
- """
- # RFC 2388 Returning Values from Forms: multipart/form-data
- # Each part is expected to contain a content-disposition header
- # [RFC 2183] where the disposition type is "form-data", and where the
- # disposition contains an (additional) parameter of "name", where the
- # value of that parameter is the original field name in the form.
- params = dict(name=name)
- if with_filenames and (filename is not None):
- # RFC 2388 Returning Values from Forms: multipart/form-data
- # The original local file name may be supplied as well, either as
- # a "filename" parameter either of the "content-disposition:
- # form-data" header or, in the case of multiple files, in a
- # "content-disposition: file" header of the subpart.
- params["filename"] = path.basename(filename)
- part.add_header("Content-Disposition", disposition, **params)
-
- def create_part(key, fileobject, content_type, multiple=False):
- """Create and return a multipart part as to given file data.
-
- key -- Field name.
- fileobject -- The file-like object to add to the part.
- content_type -- Content-type of the file. If None, use default.
- multiple -- If true, use Content-Disposition: file.
-
- """
- if not content_type:
- content_type = DEFAULT_CONTENT_TYPE
- (maintype, subtype) = content_type.split("/")
- part = MIMEBase(maintype, subtype)
- part.set_payload(fileobject.read())
- encode_base64(part)
- filename = getattr(fileobject, "name", None)
- kwargs = dict()
- if multiple:
- # RFC 2388 Returning Values from Forms: multipart/form-data
- # The original local file name may be supplied as well, either as
- # a "filename" parameter either of the "content-disposition:
- # form-data" header or, in the case of multiple files, in a
- # "content-disposition: file" header of the subpart.
- kwargs["disposition"] = "file"
- add_disposition(part, key, filename, **kwargs)
- return part
-
- # RFC 2388 Returning Values from Forms: multipart/form-data
- mime = MIMEMultipart("form-data")
- files = list()
- items_iterator = iter(content.items()) if PYTHON2 else list(content.items())
- for (key, data) in items_iterator:
- # Are there explicit encodings/content-types given?
- # Note: Cannot do a (value, encoding) = value here as fileobjects then
- # would get iterated, which is not what we want.
- if isinstance(data, tuple) and (len(data) == 2):
- (value, encoding) = data
- else:
- (value, encoding) = (data, None)
- # collect file-like objects
- if hasattr(value, "read"):
- files.append((key, value, encoding))
- # no file-like object
- else:
- if isinstance(value, MIMEBase):
- part = value
- else:
- encoding = encoding if encoding else default_encoding
- part = MIMEText(value, "plain", encoding)
- add_disposition(part, key)
- mime.attach(part)
-
- filecount = len(files)
- if filecount == 1:
- filedata = files[0]
- part = create_part(*filedata)
- mime.attach(part)
- elif filecount > 1:
- # RFC 2388 Returning Values from Forms: multipart/form-data
- # 4.2 Sets of files
- # If the value of a form field is a set of files rather than a single
- # file, that value can be transferred together using the
- # "multipart/mixed" format.
- mixed = MIMEMultipart("mixed")
- for filedata in files:
- part = create_part(multiple=True, *filedata)
- mixed.attach(part)
- mime.attach(mixed)
-
- # mime.items must be called after mime.as_string when the headers shall
- # contain the boundary
- complete_mime = mime.as_string()
- headers = dict(list(mime.items()))
- # trim headers from create mime as these will later be added by httplib.
- payload_start = complete_mime.index("\n\n") + 2
- payload = complete_mime[payload_start:]
- return (headers, payload)
-
-
-def extract_namespace(key):
- """Return the namespace in key or None, when no namespace is in key.
-
- key -- String to get namespace from
-
- """
- if not key.startswith("{"):
- return None
- return key[1:].split("}")[0]
-
-
-def get_depth(depth, allowed=("0", "1", "infinity")):
- """Return string with depth.
-
- depth -- Depth value to check.
- allowed -- Iterable with allowed depth header values.
-
- Raise ValueError, if an illegal depth was given.
-
- """
- depth = str(depth).lower()
- if depth not in allowed:
- raise ValueError("illegal depth %s" % depth)
- return depth
-
-
-def get_cookie_response(tiny_response):
- """Return response object suitable with cookielib.
-
- This makes the httplib.HTTPResponse compatible with cookielib.
-
- """
- if isinstance(tiny_response, HTTPError):
- tiny_response = tiny_response.response
- tiny_response.response.info = lambda: tiny_response.response.msg
- return tiny_response.response
-
-
-def parse_authenticate(value):
- """Parse www-authenticate header and return dict with values.
-
- Return empty dict when value doesn't match a www-authenticate header value.
-
- value -- String value of www-authenticate header.
-
- """
- sre = authparser.match(value)
- if sre:
- return sre.groupdict()
- return dict()
diff --git a/Sailfish/ownNotes/qml/cover/CoverPage.qml b/Sailfish/ownNotes/qml/cover/CoverPage.qml
index 41afa6f..47d0557 100755
--- a/Sailfish/ownNotes/qml/cover/CoverPage.qml
+++ b/Sailfish/ownNotes/qml/cover/CoverPage.qml
@@ -2,50 +2,163 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
CoverBackground {
+ id: cover
+ allowResize: true
+ property string current: ""
+ property bool preview: (current !== "")
+
+ Component.onCompleted: {
+ current = pyNotes.get('Display', 'covernote');
+ }
+
+ Connections {
+ target: pyNotes
+ onRequireRefresh: {
+ if (cover.status == Cover.Active) {
+ console.log("Require refresh")
+ cover.updateCoverText(0)
+ }
+ }
+ onNoteDeleted: {
+ if (cover.status == Cover.Active) {
+ console.log("Note delete")
+ cover.updateCoverText(0)
+ }
+ }
+ onSetCoverNote: {
+ console.log("Set cover to " + path);
+ current = path;
+ }
+ }
+
Label {
id: label
anchors.centerIn: parent
+ anchors.verticalCenterOffset: (cover.size === Cover.Small ? -Theme.paddingLarge : 0)
text: "ownNotes"
+ font {
+ pixelSize: (cover.size === Cover.Small ? Theme.fontSizeExtraSmall : Theme.fontSizeMedium)
+ }
+
+ visible: !cover.preview
}
Image {
id: icon
- source: Qt.resolvedUrl('/usr/share/ownNotes/icons/ownnotes.png')
- anchors.horizontalCenter: label.horizontalCenter
- anchors.bottom: label.top
- opacity: 0.7
+ source: (cover.size == Cover.Small ?
+ Qt.resolvedUrl('/usr/share/ownNotes/icons/coversmall.png') :
+ Qt.resolvedUrl('/usr/share/ownNotes/icons/coverlarge.png'))
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter;
+ opacity: 0.15
+ scale: 1.0
+ }
+
+ Column {
+ id: previewNote
+ width: parent.width
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.topMargin: Theme.paddingMedium
+ anchors.leftMargin: (cover.size === Cover.Small ? Theme.paddingMedium : Theme.paddingLarge)
+ anchors.rightMargin: (cover.size === Cover.Small ? Theme.paddingMedium : Theme.paddingLarge)
+ spacing: 2
+ visible: cover.preview
+
+ Text {
+ id: previewTitle
+ text: cover.current.substring(cover.current.lastIndexOf("/") + 1, cover.current.length - 4)
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ color: Theme.highlightColor
+ font {
+ pixelSize: (cover.size === Cover.Small ? Theme.fontSizeTiny : Theme.fontSizeExtraSmall)
+ family: Theme.fontFamily
+ weight: Font.Bold
+ }
+ width: parent.width
+
+ }
+
+ Item {
+ Text {
+ id: previewBody
+ text: ""
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ color: Theme.primaryColor
+ font {
+ pixelSize: Theme.fontSizeTiny
+ family: Theme.fontFamily
+ }
+ clip: true
+ width: parent.width
+ height: parent.height
+ }
+ width: parent.width
+ height: parent.height - Theme.paddingLarge - previewTitle.height
+
+ OpacityRampEffect {
+ sourceItem: previewBody
+ direction: OpacityRamp.TopToBottom
+ offset: 0.5
+ slope: 2.0
+ }
+ }
}
Label {
+ id: subNoteLabel
anchors.horizontalCenter: label.horizontalCenter
anchors.top: label.bottom
anchors.topMargin: 5
- text: qsTr("Syncing ...")
- font.family: Theme.fontFamily
- font.pixelSize: Theme.fontSizeSmall
- visible: sync.running
- opacity: visible === true ? 1.0 : 0.0
+ text: {if (sync.running === true) qsTr("Syncing ..."); else sync.get_last_sync_datetime()}
+
+ font {
+ family: Theme.fontFamily
+ pixelSize: (cover.size === Cover.Small ? Theme.fontSizeTiny : Theme.fontSizeSmall)
+ }
+ horizontalAlignment: Text.AlignHCenter
+ width: parent.width - Theme.paddingMedium
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ //visible: sync.running
+ //opacity: visible === true ? 1.0 : 0.0
+ visible: !cover.preview
+ }
+
+ onStatusChanged: {
+ if (status == Cover.Activating) {
+ console.log("Update")
+ updateCoverText(0);
+ }
}
CoverActionList {
+ id: coverActions
+
CoverAction {
- iconSource: "image://theme/icon-cover-sync"
+ iconSource: "image://theme/icon-cover-previous"
onTriggered: {
- if (sync.running === false)
- sync.launch()
+ updateCoverText(-1);
}
}
CoverAction {
- iconSource: "image://theme/icon-cover-new"
+ iconSource: "image://theme/icon-cover-next"
onTriggered: {
- appWindow.activate();
- var path = pyNotes.createNote();
- pageStack.push(
- Qt.createComponent(Qt.resolvedUrl("../pages/EditPage.qml")),
- {path:path});
+ updateCoverText(+1);
}
}
+ }
+ function updateCoverText(offset) {
+ var current = cover.current;
+ current = pyNotes.nextNoteFile(current, offset);
+ cover.current = current;
+ pyNotes.set('Display', 'covernote', current);
+ if (current !== '') {
+ var txt = pyNotes.loadPreview(current);
+ previewBody.text = txt;
+ }
}
}
diff --git a/Sailfish/ownNotes/qml/ownNotes.qml b/Sailfish/ownNotes/qml/ownNotes.qml
index d52439d..3ecc0ad 100755
--- a/Sailfish/ownNotes/qml/ownNotes.qml
+++ b/Sailfish/ownNotes/qml/ownNotes.qml
@@ -3,7 +3,6 @@ import Sailfish.Silica 1.0
import "pages"
import net.khertan.python 1.0
import Sailfish.Silica.theme 1.0
-import net.khertan.fileio 1.0
ApplicationWindow
{
@@ -15,51 +14,6 @@ ApplicationWindow
id: errorPanel
}
- FileIO {
- property string text:'';
- id: changelog
- source: "/usr/share/ownNotes/datas/changelog.html"
- onError: console.log(msg)
- }
-
- Item {
- id: aboutInfos
- property string version:VERSION
- property string contentText:qsTr('A note taking application with sync for ownCloud or any WebDav.') +
- ' ' + qsTr('Web Site : http://khertan.net/ownnotes') +
- '
Thanks to : ' +
- ' * Radek Novacek' +
- ' * caco3 on talk.maemo.org for debugging' +
- ' * Thomas Perl for PyOtherSide' +
- ' * Antoine Vacher for debugging help and tests' +
- ' * 太空飞瓜 for Chinese translation' +
- ' * Janne Edelman for Finnish translation' +
- ' * André Koot for Dutch translation' +
- ' * Equeim for Russian translation and translation patch' +
- '
Privacy Policy : ' +
- ' ownNotes can sync your notes with a webdav storage or ownCloud instance. For this ownNotes need to know the Url, Login and Password to connect to. But this is optionnal, and you can use ownNotes without the sync feature.' +
- '
' +
- 'Which datas are transmitted :' +
- ' * Login and Password will only be transmitted to the url you put in the Web Host setting.' +
- ' * When using the sync features all your notes can be transmitted to the server you put in the Web Host setting' +
- '
' +
- 'Which datas are stored :' +
- ' * All notes are stored as text files' +
- ' * An index of all files, with last synchronization datetime' +
- ' * Url & Path of the server, and login and password are stored in the settings file.' +
- '
Thanks to : ' +
+ ' * Radek Novacek' +
+ ' * caco3 on talk.maemo.org for debugging' +
+ ' * Thomas Perl for PyOtherSide' +
+ ' * Antoine Vacher for debugging help and tests' +
+ ' * 太空飞瓜 for Chinese translation' +
+ ' * Janne Edelman for Finnish translation' +
+ ' * André Koot for Dutch translation' +
+ ' * Equeim for Russian translation and translation patch' +
+ '
Privacy Policy : ' +
+ ' ownNotes can sync your notes with a webdav storage or ownCloud instance. For this ownNotes need to know the Url, Login and Password to connect to. But this is optionnal, and you can use ownNotes without the sync feature.' +
+ '
' +
+ 'Which datas are transmitted :' +
+ ' * Login and Password will only be transmitted to the url you put in the Web Host setting.' +
+ ' * When using the sync features all your notes can be transmitted to the server you put in the Web Host setting' +
+ '
' +
+ 'Which datas are stored :' +
+ ' * All notes are stored as text files' +
+ ' * An index of all files, with last synchronization datetime' +
+ ' * Url & Path of the server, and login and password are stored in the settings file.' +
+ '
' +
+ 'Markdown format :' +
+ ' For a complete documentation on the markdown format,' +
+ ' see Daringfireball Markdown Syntax. Hilighting on ownNotes support only few tags' +
+ 'of markdown syntax: title, bold, italics, links'
+ }
}
diff --git a/Sailfish/ownNotes/qml/pages/SettingsPage.qml b/Sailfish/ownNotes/qml/pages/SettingsPage.qml
index a68238b..90d000d 100755
--- a/Sailfish/ownNotes/qml/pages/SettingsPage.qml
+++ b/Sailfish/ownNotes/qml/pages/SettingsPage.qml
@@ -5,6 +5,7 @@ import net.khertan.python 1.0
Page {
id: page
+ property var fontFamilies: ["Monospace", "Serif", "Sans"];
SilicaFlickable {
id: flicker
@@ -48,7 +49,8 @@ Page {
return -1
}
Component.onCompleted: {
- var ff = Theme._fontFamilies()
+ //var ff = Theme._fontFamilies()
+ var ff = page.fontFamilies;
var s = pyNotes.get('Display', 'fontfamily');
for (var i = 0; i < ff.length; ++i) {
@@ -62,7 +64,8 @@ Page {
onCurrentIndexChanged: {
console.log('Set');
console.log(currentIndex);
- var ff = Theme._fontFamilies()
+ //var ff = Theme._fontFamilies()
+ var ff = page.fontFamilies;
console.log(ff[currentIndex]);
console.log(currentItem);
if (currentItem) {
@@ -138,14 +141,32 @@ Page {
}
TextSwitch {
- text: qsTr("Use auto merge")
- description: qsTr("When note are edited in several places, ownNotes will try to merge the changes if possible")
- checked: pyNotes.get('WebDav','merge')
+ text: qsTr("Do not verificate ssl certificate")
+ description: qsTr("Do not verify ssl certificate, this can be usefull in case of self signed certificate, but in this case you will not be protected in case of MITM Attack.")
+ checked: pyNotes.get('WebDav','nosslcheck')
onCheckedChanged: {
- pyNotes.set('WebDav','merge',checked)
+ pyNotes.set('WebDav','nosslcheck',checked)
}
}
+ TextSwitch {
+ text: qsTr("Auto Sync")
+ description: qsTr("Launch synchronisation on startup.")
+ checked: pyNotes.get('WebDav','startupsync')
+ onCheckedChanged: {
+ pyNotes.set('WebDav','startupsync',checked)
+ }
+ }
+
+
+ TextSwitch {
+ text: qsTr("Debug")
+ description: qsTr("Store sync debug logs.")
+ checked: pyNotes.get('WebDav','debug')
+ onCheckedChanged: {
+ pyNotes.set('WebDav','debug',checked)
+ }
+ }
SectionHeader {
text: 'Scriptogr.am'
diff --git a/Sailfish/ownNotes/release.py b/Sailfish/ownNotes/release.py
index 8dfd529..92c77c1 100755
--- a/Sailfish/ownNotes/release.py
+++ b/Sailfish/ownNotes/release.py
@@ -64,13 +64,13 @@ def generate_changelog(version):
print("Generate HTML Changelog")
with open("datas/changelog.html", "w") as fh:
- for version in changelog:
+ for version in sorted(changelog):
print(version)
fh.write('%s : ' % version)
for entry in changelog[version]:
fh.write('%s : %s ' % (entry['type'].title(),
entry['text'].title()))
- fh.write('
')
+ fh.write(' ')
def write_version(version, codename):
diff --git a/Sailfish/ownNotes/rpm/ownNotes.spec b/Sailfish/ownNotes/rpm/ownNotes.spec
index 59f40a8..290c21d 100755
--- a/Sailfish/ownNotes/rpm/ownNotes.spec
+++ b/Sailfish/ownNotes/rpm/ownNotes.spec
@@ -13,7 +13,7 @@ Name: ownNotes
%{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir}
Summary: A notes taking app with ownCloud Sync
-Version: 1.5.2
+Version: 1.8.2
Release: 1
Group: Qt/Qt
License: LICENSE
diff --git a/Sailfish/ownNotes/rpm/ownNotes.yaml b/Sailfish/ownNotes/rpm/ownNotes.yaml
old mode 100644
new mode 100755
index 59d75bd..5685e66
--- a/Sailfish/ownNotes/rpm/ownNotes.yaml
+++ b/Sailfish/ownNotes/rpm/ownNotes.yaml
@@ -1,6 +1,6 @@
Name: ownNotes
Summary: A notes taking app with ownCloud Sync
-Version: '1.5.2'
+Version: '1.8.2'
Release: 1
Group: Qt/Qt
URL: http://example.org/
diff --git a/Sailfish/ownNotes/src/fileio.cpp b/Sailfish/ownNotes/src/fileio.cpp
deleted file mode 100755
index 954346e..0000000
--- a/Sailfish/ownNotes/src/fileio.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "fileio.h"
-#include
-#include
-#include
-
-FileIO::FileIO(QObject *parent) :
- QObject(parent)
-{
-
-}
-
-QString FileIO::read()
-{
- if (mSource.isEmpty()){
- emit error("source is empty");
- qDebug() << "Empty source";
- return QString();
- }
-
- QFile file(mSource);
- QString fileContent;
- if ( file.open(QIODevice::ReadOnly) ) {
- fileContent = file.readAll();
- file.close();
- } else {
- emit error("Unable to open the file");
- qDebug() << "Unable to open the file";
- return QString();
- }
- return fileContent;
-}
-
-bool FileIO::write(const QString& data)
-{
- if (mSource.isEmpty())
- return false;
-
- QFile file(mSource);
- if (!file.open(QFile::WriteOnly | QFile::Truncate))
- return false;
-
- QTextStream out(&file);
- out << data;
-
- file.close();
-
- return true;
-}
diff --git a/Sailfish/ownNotes/src/fileio.h b/Sailfish/ownNotes/src/fileio.h
deleted file mode 100755
index 850e75b..0000000
--- a/Sailfish/ownNotes/src/fileio.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef FILEIO_H
-#define FILEIO_H
-
-#include
-
-class FileIO : public QObject
-{
- Q_OBJECT
-
-public:
- Q_PROPERTY(QString source
- READ source
- WRITE setSource
- NOTIFY sourceChanged)
- explicit FileIO(QObject *parent = 0);
-
- Q_INVOKABLE QString read();
- Q_INVOKABLE bool write(const QString& data);
-
- QString source() { return mSource; }
-
-public slots:
- void setSource(const QString& source) { mSource = source; }
-
-signals:
- void sourceChanged(const QString& source);
- void error(const QString& msg);
-
-private:
- QString mSource;
-};
-
-#endif // FILEIO_H
diff --git a/Sailfish/ownNotes/src/ownNotes.cpp b/Sailfish/ownNotes/src/ownNotes.cpp
index 27622de..9959983 100755
--- a/Sailfish/ownNotes/src/ownNotes.cpp
+++ b/Sailfish/ownNotes/src/ownNotes.cpp
@@ -42,7 +42,6 @@
#include
#include
#include "documenthandler.h"
-#include "fileio.h"
int main(int argc, char *argv[])
{
@@ -62,7 +61,6 @@ int main(int argc, char *argv[])
app->installTranslator(&translator);
QPython::registerQML();
qmlRegisterType("net.khertan.documenthandler", 1, 0, "DocumentHandler");
- qmlRegisterType("net.khertan.fileio", 1, 0, "FileIO");
QQuickView *view = SailfishApp::createView();
view->setSource(SailfishApp::pathTo("qml/ownNotes.qml"));
view->engine()->rootContext()->setContextProperty("VERSION", VERSION);
diff --git a/Sailfish/ownNotes/src/qpython.cpp b/Sailfish/ownNotes/src/qpython.cpp
index 8fc454a..7771075 100755
--- a/Sailfish/ownNotes/src/qpython.cpp
+++ b/Sailfish/ownNotes/src/qpython.cpp
@@ -329,7 +329,6 @@ QPython::toPython(QVariant v)
return result;
} else if (type == QVariant::String) {
QByteArray utf8bytes = v.toString().toUtf8();
- qDebug() << v.toString();
return PyUnicode_FromString(utf8bytes.constData());
} else if (type == QVariant::Map) {
QMap m = v.toMap();
@@ -348,7 +347,7 @@ QPython::toPython(QVariant v)
return PyUnicode_FromString(utf8bytes.constData());
}
- qDebug() << "XXWX Qt -> Python converstion not handled yet 2";
+ qDebug() << "XXWX Qt -> Python conversion not handled yet";
return NULL;
}
diff --git a/Sailfish/ownNotes/version.pri b/Sailfish/ownNotes/version.pri
index 9d77c0f..be90bc5 100755
--- a/Sailfish/ownNotes/version.pri
+++ b/Sailfish/ownNotes/version.pri
@@ -1,2 +1,2 @@
-VERSION=1.5.2
-CODENAME=None
+VERSION=1.8.2
+CODENAME=FlyingDuck
diff --git a/python/webdav/rfc822py3.py b/python/webdav/rfc822py3.py
new file mode 100755
index 0000000..bb61e04
--- /dev/null
+++ b/python/webdav/rfc822py3.py
@@ -0,0 +1,1013 @@
+"""RFC 2822 message manipulation (compatible with Python 3.x).
+
+Note: This is only a very rough sketch of a full RFC-822 parser; in particular
+the tokenizing of addresses does not adhere to all the quoting rules.
+
+Note: RFC 2822 is a long awaited update to RFC 822. This module should
+conform to RFC 2822, and is thus mis-named (it's not worth renaming it). Some
+effort at RFC 2822 updates have been made, but a thorough audit has not been
+performed. Consider any RFC 2822 non-conformance to be a bug.
+
+ RFC 2822: http://www.faqs.org/rfcs/rfc2822.html
+ RFC 822 : http://www.faqs.org/rfcs/rfc822.html (obsolete)
+
+Directions for use:
+
+To create a Message object: first open a file, e.g.:
+
+ fp = open(file, 'r')
+
+You can use any other legal way of getting an open file object, e.g. use
+sys.stdin or call os.popen(). Then pass the open file object to the Message()
+constructor:
+
+ m = Message(fp)
+
+This class can work with any input object that supports a readline method. If
+the input object has seek and tell capability, the rewindbody method will
+work; also illegal lines will be pushed back onto the input stream. If the
+input object lacks seek but has an `unread' method that can push back a line
+of input, Message will use that to push back illegal lines. Thus this class
+can be used to parse messages coming from a buffered stream.
+
+The optional `seekable' argument is provided as a workaround for certain stdio
+libraries in which tell() discards buffered data before discovering that the
+lseek() system call doesn't work. For maximum portability, you should set the
+seekable argument to zero to prevent that initial \code{tell} when passing in
+an unseekable object such as a a file object created from a socket object. If
+it is 1 on entry -- which it is by default -- the tell() method of the open
+file object is called once; if this raises an exception, seekable is reset to
+0. For other nonzero values of seekable, this test is not made.
+
+To get the text of a particular header there are several methods:
+
+ str = m.getheader(name)
+ str = m.getrawheader(name)
+
+where name is the name of the header, e.g. 'Subject'. The difference is that
+getheader() strips the leading and trailing whitespace, while getrawheader()
+doesn't. Both functions retain embedded whitespace (including newlines)
+exactly as they are specified in the header, and leave the case of the text
+unchanged.
+
+For addresses and address lists there are functions
+
+ realname, mailaddress = m.getaddr(name)
+ list = m.getaddrlist(name)
+
+where the latter returns a list of (realname, mailaddr) tuples.
+
+There is also a method
+
+ time = m.getdate(name)
+
+which parses a Date-like field and returns a time-compatible tuple,
+i.e. a tuple such as returned by time.localtime() or accepted by
+time.mktime().
+
+See the class definition for lower level access methods.
+
+There are also some utility functions here.
+
+- Cleanup and extensions by Eric S. Raymond
+- Ported to Python 3 by Mark J. Nenadov (Apr 16, 2011)
+
+"""
+
+import time
+
+__all__ = ["Message","AddressList","parsedate","parsedate_tz","mktime_tz"]
+
+_blanklines = ('\r\n', '\n') # Optimization for islast()
+
+
+class Message:
+ """Represents a single RFC 2822-compliant message."""
+
+ def __init__(self, fp, seekable = 1):
+ """Initialize the class instance and read the headers."""
+ if seekable == 1:
+ # Exercise tell() to make sure it works
+ # (and then assume seek() works, too)
+ try:
+ fp.tell()
+ except (AttributeError, IOError):
+ seekable = 0
+ self.fp = fp
+ self.seekable = seekable
+ self.startofheaders = None
+ self.startofbody = None
+ #
+ if self.seekable:
+ try:
+ self.startofheaders = self.fp.tell()
+ except IOError:
+ self.seekable = 0
+ #
+ self.readheaders()
+ #
+ if self.seekable:
+ try:
+ self.startofbody = self.fp.tell()
+ except IOError:
+ self.seekable = 0
+
+ def rewindbody(self):
+ """Rewind the file to the start of the body (if seekable)."""
+ if not self.seekable:
+ raise IOError("unseekable file")
+ self.fp.seek(self.startofbody)
+
+ def readheaders(self):
+ """Read header lines.
+
+ Read header lines up to the entirely blank line that terminates them.
+ The (normally blank) line that ends the headers is skipped, but not
+ included in the returned list. If a non-header line ends the headers,
+ (which is an error), an attempt is made to backspace over it; it is
+ never included in the returned list.
+
+ The variable self.status is set to the empty string if all went well,
+ otherwise it is an error message. The variable self.headers is a
+ completely uninterpreted list of lines contained in the header (so
+ printing them will reproduce the header exactly as it appears in the
+ file).
+ """
+ self.dict = {}
+ self.unixfrom = ''
+ self.headers = lst = []
+ self.status = ''
+ headerseen = ""
+ firstline = 1
+ startofline = unread = tell = None
+ if hasattr(self.fp, 'unread'):
+ unread = self.fp.unread
+ elif self.seekable:
+ tell = self.fp.tell
+ while 1:
+ if tell:
+ try:
+ startofline = tell()
+ except IOError:
+ startofline = tell = None
+ self.seekable = 0
+ line = self.fp.readline()
+ if not line:
+ self.status = 'EOF in headers'
+ break
+ # Skip unix From name time lines
+ if firstline and line.startswith('From '):
+ self.unixfrom = self.unixfrom + line
+ continue
+ firstline = 0
+ if headerseen and line[0] in ' \t':
+ # It's a continuation line.
+ lst.append(line)
+ x = (self.dict[headerseen] + "\n " + line.strip())
+ self.dict[headerseen] = x.strip()
+ continue
+ elif self.iscomment(line):
+ # It's a comment. Ignore it.
+ continue
+ elif self.islast(line):
+ # Note! No pushback here! The delimiter line gets eaten.
+ break
+ headerseen = self.isheader(line)
+ if headerseen:
+ # It's a legal header line, save it.
+ lst.append(line)
+ self.dict[headerseen] = line[len(headerseen)+1:].strip()
+ continue
+ else:
+ # It's not a header line; throw it back and stop here.
+ if not self.dict:
+ self.status = 'No headers'
+ else:
+ self.status = 'Non-header line where header expected'
+ # Try to undo the read.
+ if unread:
+ unread(line)
+ elif tell:
+ self.fp.seek(startofline)
+ else:
+ self.status = self.status + '; bad seek'
+ break
+
+ def isheader(self, line):
+ """Determine whether a given line is a legal header.
+
+ This method should return the header name, suitably canonicalized.
+ You may override this method in order to use Message parsing on tagged
+ data in RFC 2822-like formats with special header formats.
+ """
+ i = line.find(':')
+ if i > 0:
+ return line[:i].lower()
+ return None
+
+ def islast(self, line):
+ """Determine whether a line is a legal end of RFC 2822 headers.
+
+ You may override this method if your application wants to bend the
+ rules, e.g. to strip trailing whitespace, or to recognize MH template
+ separators ('--------'). For convenience (e.g. for code reading from
+ sockets) a line consisting of \r\n also matches.
+ """
+ return line in _blanklines
+
+ def iscomment(self, line):
+ """Determine whether a line should be skipped entirely.
+
+ You may override this method in order to use Message parsing on tagged
+ data in RFC 2822-like formats that support embedded comments or
+ free-text data.
+ """
+ return False
+
+ def getallmatchingheaders(self, name):
+ """Find all header lines matching a given header name.
+
+ Look through the list of headers and find all lines matching a given
+ header name (and their continuation lines). A list of the lines is
+ returned, without interpretation. If the header does not occur, an
+ empty list is returned. If the header occurs multiple times, all
+ occurrences are returned. Case is not important in the header name.
+ """
+ name = name.lower() + ':'
+ n = len(name)
+ lst = []
+ hit = 0
+ for line in self.headers:
+ if line[:n].lower() == name:
+ hit = 1
+ elif not line[:1].isspace():
+ hit = 0
+ if hit:
+ lst.append(line)
+ return lst
+
+ def getfirstmatchingheader(self, name):
+ """Get the first header line matching name.
+
+ This is similar to getallmatchingheaders, but it returns only the
+ first matching header (and its continuation lines).
+ """
+ name = name.lower() + ':'
+ n = len(name)
+ lst = []
+ hit = 0
+ for line in self.headers:
+ if hit:
+ if not line[:1].isspace():
+ break
+ elif line[:n].lower() == name:
+ hit = 1
+ if hit:
+ lst.append(line)
+ return lst
+
+ def getrawheader(self, name):
+ """A higher-level interface to getfirstmatchingheader().
+
+ Return a string containing the literal text of the header but with the
+ keyword stripped. All leading, trailing and embedded whitespace is
+ kept in the string, however. Return None if the header does not
+ occur.
+ """
+
+ lst = self.getfirstmatchingheader(name)
+ if not lst:
+ return None
+ lst[0] = lst[0][len(name) + 1:]
+ return ''.join(lst)
+
+ def getheader(self, name, default=None):
+ """Get the header value for a name.
+
+ This is the normal interface: it returns a stripped version of the
+ header value for a given header name, or None if it doesn't exist.
+ This uses the dictionary version which finds the *last* such header.
+ """
+ return self.dict.get(name.lower(), default)
+ get = getheader
+
+ def getheaders(self, name):
+ """Get all values for a header.
+
+ This returns a list of values for headers given more than once; each
+ value in the result list is stripped in the same way as the result of
+ getheader(). If the header is not given, return an empty list.
+ """
+ result = []
+ current = ''
+ have_header = 0
+ for s in self.getallmatchingheaders(name):
+ if s[0].isspace():
+ if current:
+ current = "%s\n %s" % (current, s.strip())
+ else:
+ current = s.strip()
+ else:
+ if have_header:
+ result.append(current)
+ current = s[s.find(":") + 1:].strip()
+ have_header = 1
+ if have_header:
+ result.append(current)
+ return result
+
+ def getaddr(self, name):
+ """Get a single address from a header, as a tuple.
+
+ An example return value:
+ ('Guido van Rossum', 'guido@cwi.nl')
+ """
+ # New, by Ben Escoto
+ alist = self.getaddrlist(name)
+ if alist:
+ return alist[0]
+ else:
+ return (None, None)
+
+ def getaddrlist(self, name):
+ """Get a list of addresses from a header.
+
+ Retrieves a list of addresses from a header, where each address is a
+ tuple as returned by getaddr(). Scans all named headers, so it works
+ properly with multiple To: or Cc: headers for example.
+ """
+ raw = []
+ for h in self.getallmatchingheaders(name):
+ if h[0] in ' \t':
+ raw.append(h)
+ else:
+ if raw:
+ raw.append(', ')
+ i = h.find(':')
+ if i > 0:
+ addr = h[i+1:]
+ raw.append(addr)
+ alladdrs = ''.join(raw)
+ a = AddressList(alladdrs)
+ return a.addresslist
+
+ def getdate(self, name):
+ """Retrieve a date field from a header.
+
+ Retrieves a date field from the named header, returning a tuple
+ compatible with time.mktime().
+ """
+ try:
+ data = self[name]
+ except KeyError:
+ return None
+ return parsedate(data)
+
+ def getdate_tz(self, name):
+ """Retrieve a date field from a header as a 10-tuple.
+
+ The first 9 elements make up a tuple compatible with time.mktime(),
+ and the 10th is the offset of the poster's time zone from GMT/UTC.
+ """
+ try:
+ data = self[name]
+ except KeyError:
+ return None
+ return parsedate_tz(data)
+
+
+ # Access as a dictionary (only finds *last* header of each type):
+
+ def __len__(self):
+ """Get the number of headers in a message."""
+ return len(self.dict)
+
+ def __getitem__(self, name):
+ """Get a specific header, as from a dictionary."""
+ return self.dict[name.lower()]
+
+ def __setitem__(self, name, value):
+ """Set the value of a header.
+
+ Note: This is not a perfect inversion of __getitem__, because any
+ changed headers get stuck at the end of the raw-headers list rather
+ than where the altered header was.
+ """
+ del self[name] # Won't fail if it doesn't exist
+ self.dict[name.lower()] = value
+ text = name + ": " + value
+ for line in text.split("\n"):
+ self.headers.append(line + "\n")
+
+ def __delitem__(self, name):
+ """Delete all occurrences of a specific header, if it is present."""
+ name = name.lower()
+ if not name in self.dict:
+ return
+ del self.dict[name]
+ name = name + ':'
+ n = len(name)
+ lst = []
+ hit = 0
+ for i in range(len(self.headers)):
+ line = self.headers[i]
+ if line[:n].lower() == name:
+ hit = 1
+ elif not line[:1].isspace():
+ hit = 0
+ if hit:
+ lst.append(i)
+ for i in reversed(lst):
+ del self.headers[i]
+
+ def setdefault(self, name, default=""):
+ lowername = name.lower()
+ if lowername in self.dict:
+ return self.dict[lowername]
+ else:
+ text = name + ": " + default
+ for line in text.split("\n"):
+ self.headers.append(line + "\n")
+ self.dict[lowername] = default
+ return default
+
+ def has_key(self, name):
+ """Determine whether a message contains the named header."""
+ return name.lower() in self.dict
+
+ def __contains__(self, name):
+ """Determine whether a message contains the named header."""
+ return name.lower() in self.dict
+
+ def __iter__(self):
+ return iter(self.dict)
+
+ def keys(self):
+ """Get all of a message's header field names."""
+ return list(self.dict.keys())
+
+ def values(self):
+ """Get all of a message's header field values."""
+ return list(self.dict.values())
+
+ def items(self):
+ """Get all of a message's headers.
+
+ Returns a list of name, value tuples.
+ """
+ return list(self.dict.items())
+
+ def __str__(self):
+ return ''.join(self.headers)
+
+
+# Utility functions
+# -----------------
+
+# XXX Should fix unquote() and quote() to be really conformant.
+# XXX The inverses of the parse functions may also be useful.
+
+
+def unquote(s):
+ """Remove quotes from a string."""
+ if len(s) > 1:
+ if s.startswith('"') and s.endswith('"'):
+ return s[1:-1].replace('\\\\', '\\').replace('\\"', '"')
+ if s.startswith('<') and s.endswith('>'):
+ return s[1:-1]
+ return s
+
+
+def quote(s):
+ """Add quotes around a string."""
+ return s.replace('\\', '\\\\').replace('"', '\\"')
+
+
+def parseaddr(address):
+ """Parse an address into a (realname, mailaddr) tuple."""
+ a = AddressList(address)
+ lst = a.addresslist
+ if not lst:
+ return (None, None)
+ return lst[0]
+
+
+class AddrlistClass:
+ """Address parser class by Ben Escoto.
+
+ To understand what this class does, it helps to have a copy of
+ RFC 2822 in front of you.
+
+ http://www.faqs.org/rfcs/rfc2822.html
+
+ Note: this class interface is deprecated and may be removed in the future.
+ Use rfc822.AddressList instead.
+ """
+
+ def __init__(self, field):
+ """Initialize a new instance.
+
+ `field' is an unparsed address header field, containing one or more
+ addresses.
+ """
+ self.specials = '()<>@,:;.\"[]'
+ self.pos = 0
+ self.LWS = ' \t'
+ self.CR = '\r\n'
+ self.atomends = self.specials + self.LWS + self.CR
+ # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it
+ # is obsolete syntax. RFC 2822 requires that we recognize obsolete
+ # syntax, so allow dots in phrases.
+ self.phraseends = self.atomends.replace('.', '')
+ self.field = field
+ self.commentlist = []
+
+ def gotonext(self):
+ """Parse up to the start of the next address."""
+ while self.pos < len(self.field):
+ if self.field[self.pos] in self.LWS + '\n\r':
+ self.pos = self.pos + 1
+ elif self.field[self.pos] == '(':
+ self.commentlist.append(self.getcomment())
+ else: break
+
+ def getaddrlist(self):
+ """Parse all addresses.
+
+ Returns a list containing all of the addresses.
+ """
+ result = []
+ ad = self.getaddress()
+ while ad:
+ result += ad
+ ad = self.getaddress()
+ return result
+
+ def getaddress(self):
+ """Parse the next address."""
+ self.commentlist = []
+ self.gotonext()
+
+ oldpos = self.pos
+ oldcl = self.commentlist
+ plist = self.getphraselist()
+
+ self.gotonext()
+ returnlist = []
+
+ if self.pos >= len(self.field):
+ # Bad email address technically, no domain.
+ if plist:
+ returnlist = [(' '.join(self.commentlist), plist[0])]
+
+ elif self.field[self.pos] in '.@':
+ # email address is just an addrspec
+ # this isn't very efficient since we start over
+ self.pos = oldpos
+ self.commentlist = oldcl
+ addrspec = self.getaddrspec()
+ returnlist = [(' '.join(self.commentlist), addrspec)]
+
+ elif self.field[self.pos] == ':':
+ # address is a group
+ returnlist = []
+
+ fieldlen = len(self.field)
+ self.pos += 1
+ while self.pos < len(self.field):
+ self.gotonext()
+ if self.pos < fieldlen and self.field[self.pos] == ';':
+ self.pos += 1
+ break
+ returnlist = returnlist + self.getaddress()
+
+ elif self.field[self.pos] == '<':
+ # Address is a phrase then a route addr
+ routeaddr = self.getrouteaddr()
+
+ if self.commentlist:
+ returnlist = [(' '.join(plist) + ' (' + \
+ ' '.join(self.commentlist) + ')', routeaddr)]
+ else: returnlist = [(' '.join(plist), routeaddr)]
+
+ else:
+ if plist:
+ returnlist = [(' '.join(self.commentlist), plist[0])]
+ elif self.field[self.pos] in self.specials:
+ self.pos += 1
+
+ self.gotonext()
+ if self.pos < len(self.field) and self.field[self.pos] == ',':
+ self.pos += 1
+ return returnlist
+
+ def getrouteaddr(self):
+ """Parse a route address (Return-path value).
+
+ This method just skips all the route stuff and returns the addrspec.
+ """
+ if self.field[self.pos] != '<':
+ return
+
+ expectroute = 0
+ self.pos += 1
+ self.gotonext()
+ adlist = ""
+ while self.pos < len(self.field):
+ if expectroute:
+ self.getdomain()
+ expectroute = 0
+ elif self.field[self.pos] == '>':
+ self.pos += 1
+ break
+ elif self.field[self.pos] == '@':
+ self.pos += 1
+ expectroute = 1
+ elif self.field[self.pos] == ':':
+ self.pos += 1
+ else:
+ adlist = self.getaddrspec()
+ self.pos += 1
+ break
+ self.gotonext()
+
+ return adlist
+
+ def getaddrspec(self):
+ """Parse an RFC 2822 addr-spec."""
+ aslist = []
+
+ self.gotonext()
+ while self.pos < len(self.field):
+ if self.field[self.pos] == '.':
+ aslist.append('.')
+ self.pos += 1
+ elif self.field[self.pos] == '"':
+ aslist.append('"%s"' % self.getquote())
+ elif self.field[self.pos] in self.atomends:
+ break
+ else: aslist.append(self.getatom())
+ self.gotonext()
+
+ if self.pos >= len(self.field) or self.field[self.pos] != '@':
+ return ''.join(aslist)
+
+ aslist.append('@')
+ self.pos += 1
+ self.gotonext()
+ return ''.join(aslist) + self.getdomain()
+
+ def getdomain(self):
+ """Get the complete domain name from an address."""
+ sdlist = []
+ while self.pos < len(self.field):
+ if self.field[self.pos] in self.LWS:
+ self.pos += 1
+ elif self.field[self.pos] == '(':
+ self.commentlist.append(self.getcomment())
+ elif self.field[self.pos] == '[':
+ sdlist.append(self.getdomainliteral())
+ elif self.field[self.pos] == '.':
+ self.pos += 1
+ sdlist.append('.')
+ elif self.field[self.pos] in self.atomends:
+ break
+ else: sdlist.append(self.getatom())
+ return ''.join(sdlist)
+
+ def getdelimited(self, beginchar, endchars, allowcomments = 1):
+ """Parse a header fragment delimited by special characters.
+
+ `beginchar' is the start character for the fragment. If self is not
+ looking at an instance of `beginchar' then getdelimited returns the
+ empty string.
+
+ `endchars' is a sequence of allowable end-delimiting characters.
+ Parsing stops when one of these is encountered.
+
+ If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed
+ within the parsed fragment.
+ """
+ if self.field[self.pos] != beginchar:
+ return ''
+
+ slist = ['']
+ quote = 0
+ self.pos += 1
+ while self.pos < len(self.field):
+ if quote == 1:
+ slist.append(self.field[self.pos])
+ quote = 0
+ elif self.field[self.pos] in endchars:
+ self.pos += 1
+ break
+ elif allowcomments and self.field[self.pos] == '(':
+ slist.append(self.getcomment())
+ continue # have already advanced pos from getcomment
+ elif self.field[self.pos] == '\\':
+ quote = 1
+ else:
+ slist.append(self.field[self.pos])
+ self.pos += 1
+
+ return ''.join(slist)
+
+ def getquote(self):
+ """Get a quote-delimited fragment from self's field."""
+ return self.getdelimited('"', '"\r', 0)
+
+ def getcomment(self):
+ """Get a parenthesis-delimited fragment from self's field."""
+ return self.getdelimited('(', ')\r', 1)
+
+ def getdomainliteral(self):
+ """Parse an RFC 2822 domain-literal."""
+ return '[%s]' % self.getdelimited('[', ']\r', 0)
+
+ def getatom(self, atomends=None):
+ """Parse an RFC 2822 atom.
+
+ Optional atomends specifies a different set of end token delimiters
+ (the default is to use self.atomends). This is used e.g. in
+ getphraselist() since phrase endings must not include the `.' (which
+ is legal in phrases)."""
+ atomlist = ['']
+ if atomends is None:
+ atomends = self.atomends
+
+ while self.pos < len(self.field):
+ if self.field[self.pos] in atomends:
+ break
+ else: atomlist.append(self.field[self.pos])
+ self.pos += 1
+
+ return ''.join(atomlist)
+
+ def getphraselist(self):
+ """Parse a sequence of RFC 2822 phrases.
+
+ A phrase is a sequence of words, which are in turn either RFC 2822
+ atoms or quoted-strings. Phrases are canonicalized by squeezing all
+ runs of continuous whitespace into one space.
+ """
+ plist = []
+
+ while self.pos < len(self.field):
+ if self.field[self.pos] in self.LWS:
+ self.pos += 1
+ elif self.field[self.pos] == '"':
+ plist.append(self.getquote())
+ elif self.field[self.pos] == '(':
+ self.commentlist.append(self.getcomment())
+ elif self.field[self.pos] in self.phraseends:
+ break
+ else:
+ plist.append(self.getatom(self.phraseends))
+
+ return plist
+
+class AddressList(AddrlistClass):
+ """An AddressList encapsulates a list of parsed RFC 2822 addresses."""
+ def __init__(self, field):
+ AddrlistClass.__init__(self, field)
+ if field:
+ self.addresslist = self.getaddrlist()
+ else:
+ self.addresslist = []
+
+ def __len__(self):
+ return len(self.addresslist)
+
+ def __str__(self):
+ return ", ".join(map(dump_address_pair, self.addresslist))
+
+ def __add__(self, other):
+ # Set union
+ newaddr = AddressList(None)
+ newaddr.addresslist = self.addresslist[:]
+ for x in other.addresslist:
+ if not x in self.addresslist:
+ newaddr.addresslist.append(x)
+ return newaddr
+
+ def __iadd__(self, other):
+ # Set union, in-place
+ for x in other.addresslist:
+ if not x in self.addresslist:
+ self.addresslist.append(x)
+ return self
+
+ def __sub__(self, other):
+ # Set difference
+ newaddr = AddressList(None)
+ for x in self.addresslist:
+ if not x in other.addresslist:
+ newaddr.addresslist.append(x)
+ return newaddr
+
+ def __isub__(self, other):
+ # Set difference, in-place
+ for x in other.addresslist:
+ if x in self.addresslist:
+ self.addresslist.remove(x)
+ return self
+
+ def __getitem__(self, index):
+ # Make indexing, slices, and 'in' work
+ return self.addresslist[index]
+
+def dump_address_pair(pair):
+ """Dump a (name, address) pair in a canonicalized form."""
+ if pair[0]:
+ return '"' + pair[0] + '" <' + pair[1] + '>'
+ else:
+ return pair[1]
+
+# Parse a date field
+
+_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
+ 'aug', 'sep', 'oct', 'nov', 'dec',
+ 'january', 'february', 'march', 'april', 'may', 'june', 'july',
+ 'august', 'september', 'october', 'november', 'december']
+_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
+
+# The timezone table does not include the military time zones defined
+# in RFC822, other than Z. According to RFC1123, the description in
+# RFC822 gets the signs wrong, so we can't rely on any such time
+# zones. RFC1123 recommends that numeric timezone indicators be used
+# instead of timezone names.
+
+_timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0,
+ 'AST': -400, 'ADT': -300, # Atlantic (used in Canada)
+ 'EST': -500, 'EDT': -400, # Eastern
+ 'CST': -600, 'CDT': -500, # Central
+ 'MST': -700, 'MDT': -600, # Mountain
+ 'PST': -800, 'PDT': -700 # Pacific
+ }
+
+
+def parsedate_tz(data):
+ """Convert a date string to a time tuple.
+
+ Accounts for military timezones.
+ """
+ if not data:
+ return None
+ data = data.split()
+ if data[0][-1] in (',', '.') or data[0].lower() in _daynames:
+ # There's a dayname here. Skip it
+ del data[0]
+ else:
+ # no space after the "weekday,"?
+ i = data[0].rfind(',')
+ if i >= 0:
+ data[0] = data[0][i+1:]
+ if len(data) == 3: # RFC 850 date, deprecated
+ stuff = data[0].split('-')
+ if len(stuff) == 3:
+ data = stuff + data[1:]
+ if len(data) == 4:
+ s = data[3]
+ i = s.find('+')
+ if i > 0:
+ data[3:] = [s[:i], s[i+1:]]
+ else:
+ data.append('') # Dummy tz
+ if len(data) < 5:
+ return None
+ data = data[:5]
+ [dd, mm, yy, tm, tz] = data
+ mm = mm.lower()
+ if not mm in _monthnames:
+ dd, mm = mm, dd.lower()
+ if not mm in _monthnames:
+ return None
+ mm = _monthnames.index(mm)+1
+ if mm > 12: mm = mm - 12
+ if dd[-1] == ',':
+ dd = dd[:-1]
+ i = yy.find(':')
+ if i > 0:
+ yy, tm = tm, yy
+ if yy[-1] == ',':
+ yy = yy[:-1]
+ if not yy[0].isdigit():
+ yy, tz = tz, yy
+ if tm[-1] == ',':
+ tm = tm[:-1]
+ tm = tm.split(':')
+ if len(tm) == 2:
+ [thh, tmm] = tm
+ tss = '0'
+ elif len(tm) == 3:
+ [thh, tmm, tss] = tm
+ else:
+ return None
+ try:
+ yy = int(yy)
+ dd = int(dd)
+ thh = int(thh)
+ tmm = int(tmm)
+ tss = int(tss)
+ except ValueError:
+ return None
+ tzoffset = None
+ tz = tz.upper()
+ if tz in _timezones:
+ tzoffset = _timezones[tz]
+ else:
+ try:
+ tzoffset = int(tz)
+ except ValueError:
+ pass
+ # Convert a timezone offset into seconds ; -0500 -> -18000
+ if tzoffset:
+ if tzoffset < 0:
+ tzsign = -1
+ tzoffset = -tzoffset
+ else:
+ tzsign = 1
+ tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60)
+ return (yy, mm, dd, thh, tmm, tss, 0, 1, 0, tzoffset)
+
+
+def parsedate(data):
+ """Convert a time string to a time tuple."""
+ t = parsedate_tz(data)
+ if t is None:
+ return t
+ return t[:9]
+
+
+def mktime_tz(data):
+ """Turn a 10-tuple as returned by parsedate_tz() into a UTC timestamp."""
+ if data[9] is None:
+ # No zone info, so localtime is better assumption than GMT
+ return time.mktime(data[:8] + (-1,))
+ else:
+ t = time.mktime(data[:8] + (0,))
+ return t - data[9] - time.timezone
+
+def formatdate(timeval=None):
+ """Returns time format preferred for Internet standards.
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+
+ According to RFC 1123, day and month names must always be in
+ English. If not for that, this code could use strftime(). It
+ can't because strftime() honors the locale and could generated
+ non-English names.
+ """
+ if timeval is None:
+ timeval = time.time()
+ timeval = time.gmtime(timeval)
+ return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
+ ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")[timeval[6]],
+ timeval[2],
+ ("Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")[timeval[1]-1],
+ timeval[0], timeval[3], timeval[4], timeval[5])
+
+
+# When used as script, run a small test program.
+# The first command line argument must be a filename containing one
+# message in RFC-822 format.
+
+if __name__ == '__main__':
+ import sys, os
+ if not 'HOME' in os.environ:
+ os.environ['HOME'] = os.path.join(os.environ['HOMEDRIVE'], \
+ os.environ['HOMEPATH'])
+ file = os.path.join(os.environ['HOME'], 'Mail/inbox/1')
+ if sys.argv[1:]: file = sys.argv[1]
+ f = open(file, 'r')
+ m = Message(f)
+ print(('From:', m.getaddr('from')))
+ print(('To:', m.getaddrlist('to')))
+ print(('Subject:', m.getheader('subject')))
+ print(('Date:', m.getheader('date')))
+ date = m.getdate_tz('date')
+ tz = date[-1]
+ date = time.localtime(mktime_tz(date))
+ if date:
+ print(('ParsedDate:', time.asctime(date),))
+ hhmmss = tz
+ hhmm, ss = divmod(hhmmss, 60)
+ hh, mm = divmod(hhmm, 60)
+ print(("%+03d%02d" % (hh, mm),))
+ if ss: print((".%02d" % ss,))
+ print()
+ else:
+ print(('ParsedDate:', None))
+ m.rewindbody()
+ n = 0
+ while f.readline():
+ n += 1
+ print(('Lines:', n))
+ print(('-'*70))
+ print(('len =', len(m)))
+ if 'Date' in m: print(('Date =', m['Date']))
+ if 'X-Nonsense' in m: pass
+ print(('keys =', list(m.keys())))
+ print(('values =', list(m.values())))
+ print(('items =', list(m.items())))