import project
674
COPYING
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
14
README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Remote Touchpad
|
||||
|
||||
Control mouse and keyboard from the webbrowser of a smartphone (or any other device with a touchscreen).
|
||||
To take control just open the displayed URL or scan the QR code.
|
||||
|
||||
Supports with Linux/X11 and Windows.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
363
bindata.generated.go
Normal file
3
bindata.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package main
|
||||
|
||||
//go:generate go-bindata-assetfs -o ./bindata.generated.go webdata/...
|
||||
35
desktop/remote-touchpad.appdata.xml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>remote-touchpad.desktop</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>AGPL-3.0-or-later</project_license>
|
||||
<name>Remote Touchpad</name>
|
||||
<summary>Control mouse and keyboard from a smartphone</summary>
|
||||
<description>
|
||||
<p>
|
||||
Control mouse and keyboard from the webbrowser of a smartphone (or any other device with a touchscreen).
|
||||
To take control just open the displayed URL or scan the QR code.
|
||||
</p>
|
||||
<p>
|
||||
Supports Linux/X11 and Windows.
|
||||
</p>
|
||||
</description>
|
||||
<url type="bugtracker">https://github.com/Unrud/remote-touchpad/issues</url>
|
||||
<url type="homepage">https://github.com/Unrud/remote-touchpad</url>
|
||||
<developer_name>Unrud</developer_name>
|
||||
<releases>
|
||||
<release version="0.0.1" date="2018-06-23" />
|
||||
</releases>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/Unrud/remote-touchpad/0.0.1/screenshots/1.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/Unrud/remote-touchpad/0.0.1/screenshots/2.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/Unrud/remote-touchpad/0.0.1/screenshots/3.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1" />
|
||||
</component>
|
||||
9
desktop/remote-touchpad.desktop
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Remote Touchpad
|
||||
Comment=Control mouse and keyboard from a smartphone
|
||||
Terminal=true
|
||||
Exec=remote-touchpad
|
||||
Icon=remote-touchpad
|
||||
Categories=Network;RemoteAccess;ConsoleOnly;
|
||||
BIN
desktop/remote-touchpad.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
desktop/remote-touchpad_128.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
desktop/remote-touchpad_256.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
desktop/remote-touchpad_512.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
desktop/remote-touchpad_64.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
113
flatpak/com.github.unrud.RemoteTouchpad.json
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
{
|
||||
"app-id" : "com.github.unrud.RemoteTouchpad",
|
||||
"runtime" : "org.freedesktop.Platform",
|
||||
"runtime-version" : "1.6",
|
||||
"sdk" : "org.freedesktop.Sdk",
|
||||
"command" : "remote-touchpad",
|
||||
"rename-appdata-file" : "remote-touchpad.appdata.xml",
|
||||
"rename-desktop-file" : "remote-touchpad.desktop",
|
||||
"rename-icon" : "remote-touchpad",
|
||||
"finish-args" : [
|
||||
"--socket=x11",
|
||||
"--share=network"
|
||||
],
|
||||
"build-options" : {
|
||||
"append-path" : "/app/share/go/bin",
|
||||
"env" : {
|
||||
}
|
||||
},
|
||||
"modules" : [
|
||||
{
|
||||
"name" : "go-compiler",
|
||||
"sources" : [
|
||||
{
|
||||
"only-arches" : [
|
||||
"i386"
|
||||
],
|
||||
"type" : "archive",
|
||||
"strip-components" : 0,
|
||||
"url" : "https://dl.google.com/go/go1.10.3.linux-386.tar.gz",
|
||||
"sha256" : "3d5fe1932c904a01acb13dae07a5835bffafef38bef9e5a05450c52948ebdeb4"
|
||||
},
|
||||
{
|
||||
"only-arches" : [
|
||||
"x86_64"
|
||||
],
|
||||
"type" : "archive",
|
||||
"strip-components" : 0,
|
||||
"url" : "https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz",
|
||||
"sha256" : "fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035"
|
||||
},
|
||||
{
|
||||
"only-arches" : [
|
||||
"arm"
|
||||
],
|
||||
"type" : "archive",
|
||||
"strip-components" : 0,
|
||||
"url" : "https://dl.google.com/go/go1.10.3.linux-armv6l.tar.gz",
|
||||
"sha256" : "d3df3fa3d153e81041af24f31a82f86a21cb7b92c1b5552fb621bad0320f06b6"
|
||||
},
|
||||
{
|
||||
"only-arches" : [
|
||||
"aarch64"
|
||||
],
|
||||
"type" : "archive",
|
||||
"strip-components" : 0,
|
||||
"url" : "https://dl.google.com/go/go1.10.3.linux-arm64.tar.gz",
|
||||
"sha256" : "355128a05b456c9e68792143801ad18e0431510a53857f640f7b30ba92624ed2"
|
||||
}
|
||||
],
|
||||
"buildsystem" : "simple",
|
||||
"build-commands" : [
|
||||
"mkdir -p /app/share",
|
||||
"cp -r go /app/share"
|
||||
],
|
||||
"build-options" : {
|
||||
"no-debuginfo" : true
|
||||
},
|
||||
"cleanup" : [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "remote-desktop",
|
||||
"sources" : [
|
||||
{
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/golang/net",
|
||||
"commit" : "afe8f62b1d6bbd81f31868121a50b06d8188e1f9",
|
||||
"dest" : "src/golang.org/x/net"
|
||||
},
|
||||
{
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/skip2/go-qrcode",
|
||||
"commit" : "cf5f9fa2f0d847edb8e038db7ed975e239095e1a",
|
||||
"dest" : "src/github.com/skip2/go-qrcode"
|
||||
},
|
||||
{
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/elazarl/go-bindata-assetfs",
|
||||
"commit" : "38087fe4dafb822e541b3f7955075cc1c30bd294",
|
||||
"dest" : "src/github.com/elazarl/go-bindata-assetfs"
|
||||
},
|
||||
{
|
||||
"type" : "git",
|
||||
"path" : "..",
|
||||
"branch" : "master",
|
||||
"dest" : "src/github.com/unrud/remote-touchpad"
|
||||
}
|
||||
],
|
||||
"buildsystem" : "simple",
|
||||
"build-commands" : [
|
||||
"env GOPATH=\"$(pwd)\" go build github.com/unrud/remote-touchpad",
|
||||
"install -Dm0755 -t /app/bin remote-touchpad",
|
||||
"install -Dm0644 -t /app/share/appdata src/github.com/unrud/remote-touchpad/desktop/remote-touchpad.appdata.xml",
|
||||
"install -Dm0644 -t /app/share/applications src/github.com/unrud/remote-touchpad/desktop/remote-touchpad.desktop",
|
||||
"install -Dm0644 src/github.com/unrud/remote-touchpad/desktop/remote-touchpad_64.png /app/share/icons/hicolor/64x64/apps/remote-touchpad.png",
|
||||
"install -Dm0644 src/github.com/unrud/remote-touchpad/desktop/remote-touchpad_128.png /app/share/icons/hicolor/128x128/apps/remote-touchpad.png",
|
||||
"install -Dm0644 src/github.com/unrud/remote-touchpad/desktop/remote-touchpad_256.png /app/share/icons/hicolor/256x256/apps/remote-touchpad.png",
|
||||
"install -Dm0644 src/github.com/unrud/remote-touchpad/desktop/remote-touchpad_512.png /app/share/icons/hicolor/512x512/apps/remote-touchpad.png"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
78
host.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func FindDefaultHost() string {
|
||||
fallbackAddr := ""
|
||||
for _, publicIP := range []string{"2001:4860:4860::8888", "8.8.8.8"} {
|
||||
addr := fmt.Sprintf("[%s]:80", publicIP)
|
||||
conn, err := net.Dial("udp", addr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
conn.Close()
|
||||
host, _, err := net.SplitHostPort(conn.LocalAddr().String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return host
|
||||
}
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
goto fallback
|
||||
}
|
||||
for _, inter := range interfaces {
|
||||
if inter.Flags&net.FlagUp == 0 || inter.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
addrs, err := inter.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
addrs:
|
||||
for _, addr := range addrs {
|
||||
ip, _, err := net.ParseCIDR(addr.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if fallbackAddr == "" {
|
||||
fallbackAddr = ip.String()
|
||||
}
|
||||
for _, linkLocalPrefix := range []string{"169.254.", "fe8", "fe9", "fea", "feb"} {
|
||||
if strings.HasPrefix(ip.String(), linkLocalPrefix) {
|
||||
continue addrs
|
||||
}
|
||||
}
|
||||
return ip.String()
|
||||
}
|
||||
|
||||
}
|
||||
fallback:
|
||||
if fallbackAddr != "" {
|
||||
return fallbackAddr
|
||||
}
|
||||
return "localhost"
|
||||
}
|
||||
3
icon.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package main
|
||||
|
||||
//go:generate rsrc -ico desktop/remote-touchpad.ico -o icon.syso
|
||||
BIN
icon_windows.syso
Normal file
228
main.go
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"golang.org/x/net/websocket"
|
||||
"log"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSecretLength int = 8
|
||||
authenticationRateLimit time.Duration = time.Second / 10
|
||||
authenticationRateBurst int = 10
|
||||
challengeLength int = 8
|
||||
defaultBind string = ":0"
|
||||
version string = "0.0.1"
|
||||
)
|
||||
|
||||
func processCommand(plugin Plugin, command string) error {
|
||||
commandParts := strings.Split(command, " ")
|
||||
if commandParts[0] == "KeyboardText" {
|
||||
text := strings.Join(commandParts[1:], " ")
|
||||
return plugin.KeyboardText(text)
|
||||
}
|
||||
if len(commandParts) != 3 {
|
||||
return errors.New("wrong number of arguments")
|
||||
}
|
||||
x, err := strconv.ParseInt(commandParts[1], 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
y, err := strconv.ParseInt(commandParts[2], 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if commandParts[0] == "PointerMove" {
|
||||
return plugin.PointerMove(int(x), int(y))
|
||||
}
|
||||
if commandParts[0] == "PointerScroll" {
|
||||
return plugin.PointerScroll(int(x), int(y))
|
||||
}
|
||||
if commandParts[0] == "PointerButton" {
|
||||
if x <= 0 || x > 3 {
|
||||
return errors.New("unknown pointer button")
|
||||
}
|
||||
b := true
|
||||
if y == 0 {
|
||||
b = false
|
||||
}
|
||||
return plugin.PointerButton(uint(x), b)
|
||||
}
|
||||
return errors.New("unknown command")
|
||||
}
|
||||
|
||||
type challenge struct {
|
||||
message, expectedResponse string
|
||||
}
|
||||
|
||||
func (c challenge) verify(response string) bool {
|
||||
return c.expectedResponse == response
|
||||
}
|
||||
|
||||
func authenticationChallengeGenerator(secret string, challenges chan<- challenge) {
|
||||
unsecureSource := mathrand.NewSource(time.Now().UnixNano())
|
||||
unsecureRand := mathrand.New(unsecureSource)
|
||||
b := make([]byte, challengeLength)
|
||||
for {
|
||||
if _, err := unsecureRand.Read(b[:]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
message := base64.StdEncoding.EncodeToString(b[:])
|
||||
mac := hmac.New(sha256.New, []byte(message))
|
||||
mac.Write([]byte(secret))
|
||||
challenges <- challenge{
|
||||
message: message,
|
||||
expectedResponse: base64.StdEncoding.EncodeToString(mac.Sum(nil)),
|
||||
}
|
||||
time.Sleep(authenticationRateLimit)
|
||||
}
|
||||
}
|
||||
|
||||
func secureRandBase64(length int) string {
|
||||
b := make([]byte, length)
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(b[:])
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bind, certFile, keyFile, secret string
|
||||
var showVersion, invert bool
|
||||
flag.BoolVar(&showVersion, "version", false, "show program's version number and exit")
|
||||
flag.StringVar(&bind, "bind", defaultBind, "bind server to [IP]:PORT")
|
||||
flag.StringVar(&secret, "secret", "", "shared secret for client authentication")
|
||||
flag.StringVar(&certFile, "cert", "", "file containing TLS certificate")
|
||||
flag.StringVar(&keyFile, "key", "", "file containing TLS private key")
|
||||
flag.BoolVar(&invert, "invert", false, "use inverse colors for QR code")
|
||||
flag.Parse()
|
||||
if showVersion {
|
||||
fmt.Println(version)
|
||||
return
|
||||
}
|
||||
if certFile != "" && keyFile == "" {
|
||||
log.Fatal("TLS certificate missing")
|
||||
}
|
||||
if certFile == "" && keyFile != "" {
|
||||
log.Fatal("TLS private key missing")
|
||||
}
|
||||
tls := certFile != "" && keyFile != ""
|
||||
if secret == "" {
|
||||
secret = secureRandBase64(defaultSecretLength)
|
||||
}
|
||||
var plugin Plugin
|
||||
platformErrors := ""
|
||||
for _, f := range Plugins {
|
||||
var err error
|
||||
plugin, err = f()
|
||||
if err == nil {
|
||||
break
|
||||
} else if _, ok := err.(UnsupportedPlatformError); ok {
|
||||
platformErrors += err.Error() + "\n"
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
if plugin == nil {
|
||||
log.Fatal("unsupported platform:\n" + platformErrors)
|
||||
}
|
||||
defer plugin.Close()
|
||||
authenticationChallenges := make(chan challenge, authenticationRateBurst)
|
||||
go authenticationChallengeGenerator(secret, authenticationChallenges)
|
||||
listener, err := net.Listen("tcp", bind)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
addr := listener.Addr().(*net.TCPAddr)
|
||||
host := ""
|
||||
bindHost, _, err := net.SplitHostPort(bind)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, b := range addr.IP {
|
||||
if b != 0 {
|
||||
host = bindHost
|
||||
break
|
||||
}
|
||||
}
|
||||
if host == "" {
|
||||
host = FindDefaultHost()
|
||||
}
|
||||
port := addr.Port
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", http.FileServer(assetFS()))
|
||||
mux.Handle("/ws", websocket.Handler(func(ws *websocket.Conn) {
|
||||
var message string
|
||||
challenge := <-authenticationChallenges
|
||||
websocket.Message.Send(ws, challenge.message)
|
||||
if err := websocket.Message.Receive(ws, &message); err != nil {
|
||||
return
|
||||
}
|
||||
if !challenge.verify(message) {
|
||||
return
|
||||
}
|
||||
for {
|
||||
if err := websocket.Message.Receive(ws, &message); err != nil {
|
||||
return
|
||||
}
|
||||
if err := processCommand(plugin, message); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}))
|
||||
domain := host
|
||||
if port != 80 && !tls || port != 443 && tls {
|
||||
domain = net.JoinHostPort(host, strconv.Itoa(port))
|
||||
}
|
||||
scheme := "http"
|
||||
if tls {
|
||||
scheme = "https"
|
||||
}
|
||||
url := fmt.Sprintf("%s://%s/#%s\n", scheme, domain, secret)
|
||||
readyMsg := "ready: " + url
|
||||
qrCode, _ := GenerateQRCode(url, invert)
|
||||
readyMsg += qrCode
|
||||
if !tls {
|
||||
readyMsg += ("" +
|
||||
"▌ WARNING: TLS is not enabled ▐\n" +
|
||||
"▌Don't use in an untrusted network!▐\n")
|
||||
}
|
||||
log.Print(readyMsg)
|
||||
if tls {
|
||||
err = http.ServeTLS(listener, mux, certFile, keyFile)
|
||||
} else {
|
||||
err = http.Serve(listener, mux)
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
44
plugin.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var Plugins [](func() (Plugin, error)) = [](func() (Plugin, error)){
|
||||
InitX11Plugin,
|
||||
InitWindowsPlugin,
|
||||
}
|
||||
|
||||
type UnsupportedPlatformError struct {
|
||||
name string
|
||||
message string
|
||||
}
|
||||
|
||||
func (e UnsupportedPlatformError) Error() string {
|
||||
return fmt.Sprintf("%s plugin: %s", e.name, e.message)
|
||||
}
|
||||
|
||||
type Plugin interface {
|
||||
Close() error
|
||||
KeyboardText(text string) error
|
||||
PointerButton(button uint, press bool) error
|
||||
PointerMove(deltaX, deltaY int) error
|
||||
PointerScroll(stepsHorizontal, stepsVertical int) error
|
||||
}
|
||||
169
plugin_windows.go
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
// +build windows
|
||||
|
||||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
inputMouse uintptr = 0x0
|
||||
inputKeyboard uintptr = 0x1
|
||||
|
||||
keyeventfKeyup uint32 = 0x2
|
||||
keyeventfUnicode uint32 = 0x4
|
||||
|
||||
mouseeventfMove uint32 = 0x1
|
||||
mouseeventfLeftdown uint32 = 0x2
|
||||
mouseeventfLeftup uint32 = 0x4
|
||||
mouseeventfRightdown uint32 = 0x8
|
||||
mouseeventfRightup uint32 = 0x10
|
||||
mouseeventfMiddledown uint32 = 0x20
|
||||
mouseeventfMiddleup uint32 = 0x40
|
||||
mouseeventfWheel uint32 = 0x800
|
||||
mouseeventfHwheel uint32 = 0x1000
|
||||
|
||||
wheelDelta int = 120
|
||||
)
|
||||
|
||||
type mouseInput struct {
|
||||
typ uintptr // HACK: padded uint32
|
||||
|
||||
dx, dy int32
|
||||
mouseData, dwFlags, time uint32
|
||||
dwExtraInfo uintptr
|
||||
}
|
||||
|
||||
type keybdInput struct {
|
||||
typ uintptr // HACK: padded uint32
|
||||
|
||||
wVk, wScan uint16
|
||||
dwFlags, time uint32
|
||||
dwExtraInfo uintptr
|
||||
|
||||
padding [8]byte
|
||||
}
|
||||
|
||||
type windowsPlugin struct {
|
||||
userDLL *syscall.LazyDLL
|
||||
sendInputProc *syscall.LazyProc
|
||||
}
|
||||
|
||||
func InitWindowsPlugin() (Plugin, error) {
|
||||
p := &windowsPlugin{}
|
||||
p.userDLL = syscall.NewLazyDLL("user32.dll")
|
||||
p.sendInputProc = p.userDLL.NewProc("SendInput")
|
||||
if err := p.sendInputProc.Find(); err != nil {
|
||||
return nil, UnsupportedPlatformError{"Windows", err.Error()}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *windowsPlugin) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *windowsPlugin) KeyboardText(text string) error {
|
||||
runes := []rune(text)
|
||||
if len(runes) == 0 {
|
||||
return nil
|
||||
}
|
||||
inputs := make([]keybdInput, len(runes)*2)
|
||||
for i := range inputs {
|
||||
inputs[i].typ = inputKeyboard
|
||||
inputs[i].wScan = uint16(runes[i/2])
|
||||
inputs[i].dwFlags = keyeventfUnicode
|
||||
if i%2 == 1 {
|
||||
inputs[i].dwFlags |= keyeventfKeyup
|
||||
}
|
||||
}
|
||||
if r, _, err := p.sendInputProc.Call(uintptr(len(inputs)), uintptr(unsafe.Pointer(&inputs[0])), unsafe.Sizeof(inputs[0])); int(r) != len(inputs) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *windowsPlugin) PointerButton(button uint, press bool) error {
|
||||
if button == 0 || button > 3 {
|
||||
return errors.New("unsupported pointer button")
|
||||
}
|
||||
input := mouseInput{
|
||||
typ: inputMouse,
|
||||
}
|
||||
if button == 1 && press {
|
||||
input.dwFlags = mouseeventfLeftdown
|
||||
} else if button == 1 {
|
||||
input.dwFlags = mouseeventfLeftup
|
||||
} else if button == 2 && press {
|
||||
input.dwFlags = mouseeventfMiddledown
|
||||
} else if button == 2 {
|
||||
input.dwFlags = mouseeventfMiddleup
|
||||
} else if button == 3 && press {
|
||||
input.dwFlags = mouseeventfRightdown
|
||||
} else if button == 3 {
|
||||
input.dwFlags = mouseeventfRightup
|
||||
}
|
||||
if r, _, err := p.sendInputProc.Call(1, uintptr(unsafe.Pointer(&input)), unsafe.Sizeof(input)); int(r) != 1 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *windowsPlugin) PointerMove(deltaX, deltaY int) error {
|
||||
input := mouseInput{
|
||||
typ: inputMouse,
|
||||
dx: int32(deltaX),
|
||||
dy: int32(deltaY),
|
||||
dwFlags: mouseeventfMove,
|
||||
}
|
||||
if r, _, err := p.sendInputProc.Call(1, uintptr(unsafe.Pointer(&input)), unsafe.Sizeof(input)); int(r) != 1 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *windowsPlugin) PointerScroll(stepsHorizontal, stepsVertical int) error {
|
||||
inputs := make([]mouseInput, 0, 2)
|
||||
if stepsHorizontal != 0 {
|
||||
inputs = append(inputs, mouseInput{
|
||||
typ: inputMouse,
|
||||
dwFlags: mouseeventfHwheel,
|
||||
mouseData: uint32(stepsHorizontal * wheelDelta),
|
||||
})
|
||||
}
|
||||
if stepsVertical != 0 {
|
||||
inputs = append(inputs, mouseInput{
|
||||
typ: inputMouse,
|
||||
dwFlags: mouseeventfWheel,
|
||||
mouseData: uint32(stepsVertical * wheelDelta),
|
||||
})
|
||||
}
|
||||
if len(inputs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if r, _, err := p.sendInputProc.Call(uintptr(len(inputs)), uintptr(unsafe.Pointer(&inputs[0])), unsafe.Sizeof(inputs[0])); int(r) != len(inputs) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
26
plugin_windows_dummy.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// +build !windows
|
||||
|
||||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
func InitWindowsPlugin() (Plugin, error) {
|
||||
return nil, UnsupportedPlatformError{"Windows", "disabled"}
|
||||
}
|
||||
173
plugin_x11.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
// #cgo LDFLAGS: -lX11 -lXtst
|
||||
// #include <X11/Xlib.h>
|
||||
// #include <X11/Intrinsic.h>
|
||||
// #include <X11/extensions/XTest.h>
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const typingDelay time.Duration = 100 * time.Millisecond
|
||||
|
||||
type x11Plugin struct {
|
||||
display *C.Display
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func InitX11Plugin() (Plugin, error) {
|
||||
display := C.XOpenDisplay(nil)
|
||||
if display == nil {
|
||||
return nil, UnsupportedPlatformError{"X11", "failed to connect to X server"}
|
||||
}
|
||||
return &x11Plugin{display: display}, nil
|
||||
}
|
||||
|
||||
func (p *x11Plugin) Close() error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
if p.display == nil {
|
||||
return errors.New("X server connection closed")
|
||||
}
|
||||
C.XCloseDisplay(p.display)
|
||||
p.display = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *x11Plugin) KeyboardText(text string) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
if p.display == nil {
|
||||
return errors.New("X server connection closed")
|
||||
}
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
var minKeycodes, maxKeycodes C.int
|
||||
C.XDisplayKeycodes(p.display, &minKeycodes, &maxKeycodes)
|
||||
var keysymsPerKeycode C.int
|
||||
keysyms := C.XGetKeyboardMapping(p.display, C.KeyCode(minKeycodes), maxKeycodes-minKeycodes+1, &keysymsPerKeycode)
|
||||
if keysyms == nil {
|
||||
return errors.New("failed to get keyboard mapping")
|
||||
}
|
||||
emptyKeycode := C.KeyCode(0)
|
||||
keycodes:
|
||||
for keycode := C.KeyCode(minKeycodes); keycode <= C.KeyCode(maxKeycodes); keycode++ {
|
||||
for i := 0; i < int(keysymsPerKeycode); i++ {
|
||||
keysymsIndex := int(keycode-C.KeyCode(minKeycodes))*int(keysymsPerKeycode) + i
|
||||
keysym := *(*C.KeySym)(unsafe.Pointer(uintptr(unsafe.Pointer(keysyms)) + uintptr(keysymsIndex)*unsafe.Sizeof(*keysyms)))
|
||||
if keysym != 0 {
|
||||
continue keycodes
|
||||
}
|
||||
}
|
||||
emptyKeycode = keycode
|
||||
break
|
||||
}
|
||||
C.XFree(unsafe.Pointer(keysyms))
|
||||
if emptyKeycode == 0 {
|
||||
return errors.New("no empty keycode found")
|
||||
}
|
||||
keycodeMapping := make([]C.KeySym, keysymsPerKeycode)
|
||||
for _, runeValue := range text {
|
||||
keysym := C.KeySym(0x01000000 + runeValue)
|
||||
for i := range keycodeMapping {
|
||||
keycodeMapping[i] = keysym
|
||||
}
|
||||
C.XChangeKeyboardMapping(p.display, C.int(emptyKeycode), keysymsPerKeycode, (*C.KeySym)(unsafe.Pointer(&keycodeMapping[0])), 1)
|
||||
C.XTestFakeKeyEvent(p.display, C.uint(emptyKeycode), C.True, 0)
|
||||
C.XTestFakeKeyEvent(p.display, C.uint(emptyKeycode), C.False, 0)
|
||||
// race condition!
|
||||
C.XFlush(p.display)
|
||||
time.Sleep(typingDelay)
|
||||
}
|
||||
for i := range keycodeMapping {
|
||||
keycodeMapping[i] = 0
|
||||
}
|
||||
C.XChangeKeyboardMapping(p.display, C.int(emptyKeycode), keysymsPerKeycode, (*C.KeySym)(unsafe.Pointer(&keycodeMapping[0])), 1)
|
||||
C.XFlush(p.display)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *x11Plugin) PointerButton(button uint, press bool) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
if p.display == nil {
|
||||
return errors.New("X server connection closed")
|
||||
}
|
||||
if button == 0 || button > 9 {
|
||||
return errors.New("unsupported pointer button")
|
||||
}
|
||||
var pressC C.int = C.False
|
||||
if press {
|
||||
pressC = C.True
|
||||
}
|
||||
C.XTestFakeButtonEvent(p.display, C.uint(button), pressC, 0)
|
||||
C.XFlush(p.display)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *x11Plugin) PointerMove(deltaX, deltaY int) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
if p.display == nil {
|
||||
return errors.New("X server connection closed")
|
||||
}
|
||||
C.XTestFakeRelativeMotionEvent(p.display, C.int(deltaX), C.int(deltaY), 0)
|
||||
C.XFlush(p.display)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *x11Plugin) PointerScroll(stepsHorizontal, stepsVertical int) error {
|
||||
var buttonHorizontal uint = 7
|
||||
if stepsHorizontal < 0 {
|
||||
buttonHorizontal = 6
|
||||
stepsHorizontal = -stepsHorizontal
|
||||
}
|
||||
for i := 0; i < stepsHorizontal; i++ {
|
||||
if err := p.PointerButton(buttonHorizontal, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.PointerButton(buttonHorizontal, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var buttonVertical uint = 5
|
||||
if stepsVertical < 0 {
|
||||
buttonVertical = 4
|
||||
stepsVertical = -stepsVertical
|
||||
}
|
||||
for i := 0; i < stepsVertical; i++ {
|
||||
if err := p.PointerButton(buttonVertical, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.PointerButton(buttonVertical, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
26
plugin_x11_dummy.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
func InitX11Plugin() (Plugin, error) {
|
||||
return nil, UnsupportedPlatformError{"X11", "disabled"}
|
||||
}
|
||||
57
qr.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import qrcode "github.com/skip2/go-qrcode"
|
||||
|
||||
func GenerateQRCode(message string, invert bool) (string, error) {
|
||||
q, err := qrcode.New(message, qrcode.Medium)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return qrCodeToString(q.Bitmap(), invert), nil
|
||||
}
|
||||
|
||||
func qrCodeToString(bits [][]bool, invert bool) string {
|
||||
s := ""
|
||||
for y := -1; y < len(bits); y += 2 {
|
||||
for x := range bits[0] {
|
||||
upper := !invert
|
||||
if 0 <= y && y < len(bits) {
|
||||
upper = bits[y][x]
|
||||
}
|
||||
lower := !invert
|
||||
if 0 <= y+1 && y+1 < len(bits) {
|
||||
lower = bits[y+1][x]
|
||||
}
|
||||
if upper != invert && lower != invert {
|
||||
s += " "
|
||||
} else if upper == invert && lower != invert {
|
||||
s += "▀"
|
||||
} else if upper != invert && lower == invert {
|
||||
s += "▄"
|
||||
} else {
|
||||
s += "█"
|
||||
}
|
||||
}
|
||||
s += "\n"
|
||||
}
|
||||
return s
|
||||
}
|
||||
BIN
screenshots/1.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
screenshots/2.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
screenshots/3.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
385
webdata/fn.js
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Unrud<unrud@outlook.com>
|
||||
*
|
||||
* This file is part of Remote-Touchpad.
|
||||
*
|
||||
* Foobar 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.
|
||||
*
|
||||
* Remote-Touchpad 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// [1 Touch, 2 Touches, 3 Touches]
|
||||
const TOUCH_MOVE_THRESHOLD = [10, 15, 15];
|
||||
const TOUCH_TIMEOUT = 250;
|
||||
const MOVE_MULT = 1;
|
||||
const SCROLL_MULT = 0.05;
|
||||
// [[px/s, mult], ...]
|
||||
const POINTER_ACCELERATION = [[0, 0], [87, 1], [173, 1], [553, 2]];
|
||||
const UPDATE_INTERVAL = 50;
|
||||
const MAX_IDLE_UPDATES = 10;
|
||||
|
||||
var ws;
|
||||
var pad;
|
||||
var padlabel;
|
||||
|
||||
var touchMoved = false;
|
||||
var touchStart = 0;
|
||||
var touchLastEnd = 0;
|
||||
var touchReleasedCount = 0;
|
||||
var ongoingTouches = [];
|
||||
var moveXSum = 0;
|
||||
var moveYSum = 0;
|
||||
var scrollXSum = 0;
|
||||
var scrollYSum = 0;
|
||||
var dragging = false;
|
||||
var draggingTimeout = null;
|
||||
var updateTimer = null;
|
||||
var idleUpdates = 0;
|
||||
|
||||
function fullscreenEnabled() {
|
||||
return (document.fullscreenEnabled ||
|
||||
document.webkitFullscreenEnabled ||
|
||||
document.mozFullScreenEnabled ||
|
||||
document.msFullscreenEnabled ||
|
||||
false);
|
||||
}
|
||||
|
||||
function requestFullscreen(e) {
|
||||
if (e.requestFullscreen) {
|
||||
e.requestFullscreen();
|
||||
} else if (e.webkitRequestFullscreen) {
|
||||
e.webkitRequestFullscreen();
|
||||
} else if (e.mozRequestFullScreen) {
|
||||
e.mozRequestFullScreen();
|
||||
} else if (e.msRequestFullscreen) {
|
||||
e.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
function exitFullscreen() {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
function fullscreenElement() {
|
||||
return (document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.msFullscreenElement ||
|
||||
null);
|
||||
}
|
||||
|
||||
function addFullscreenchangeEventListener(listener) {
|
||||
if (document.fullscreenElement !== undefined) {
|
||||
document.addEventListener("fullscreenchange", listener);
|
||||
} else if (document.webkitFullscreenElement !== undefined) {
|
||||
document.addEventListener("webkitfullscreenchange", listener);
|
||||
} else if (document.mozFullScreenElement !== undefined) {
|
||||
document.addEventListener("mozfullscreenchange", listener);
|
||||
} else if (document.msFullscreenElement !== undefined) {
|
||||
document.addEventListener("MSFullscreenChange", listener);
|
||||
}
|
||||
}
|
||||
|
||||
function copyTouch(touch, timeStamp) {
|
||||
return {
|
||||
identifier: touch.identifier,
|
||||
pageX: touch.pageX,
|
||||
pageXStart: touch.pageX,
|
||||
pageY: touch.pageY,
|
||||
pageYStart: touch.pageY,
|
||||
timeStamp: timeStamp
|
||||
};
|
||||
}
|
||||
|
||||
function ongoingTouchIndexById(idToFind) {
|
||||
for (var i = 0; i < ongoingTouches.length; i++) {
|
||||
var id = ongoingTouches[i].identifier;
|
||||
|
||||
if (id == idToFind) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function calculatePointerAccelerationMult(speed) {
|
||||
for (var i = 0; i < POINTER_ACCELERATION.length; i++) {
|
||||
s2 = POINTER_ACCELERATION[i][0];
|
||||
a2 = POINTER_ACCELERATION[i][1];
|
||||
if (s2 <= speed) {
|
||||
continue;
|
||||
}
|
||||
if (i == 0) {
|
||||
return a2;
|
||||
}
|
||||
s1 = POINTER_ACCELERATION[i-1][0];
|
||||
a1 = POINTER_ACCELERATION[i-1][1];
|
||||
return ((speed-s1) / (s2-s1)) * (a2-a1) + a1;
|
||||
}
|
||||
if (POINTER_ACCELERATION.length > 0) {
|
||||
return POINTER_ACCELERATION[POINTER_ACCELERATION.length-1][1];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
function onDraggingTimeout() {
|
||||
draggingTimeout = null;
|
||||
ws.send("PointerButton 1 0");
|
||||
}
|
||||
|
||||
function updateMoveAndScroll() {
|
||||
if (updateTimer == null) {
|
||||
onUpdateTimeout();
|
||||
if (UPDATE_INTERVAL > 0 && idleUpdates == 0) {
|
||||
updateTimer = setInterval(onUpdateTimeout, UPDATE_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onUpdateTimeout() {
|
||||
idleUpdates += 1;
|
||||
var moveX = Math.trunc(moveXSum);
|
||||
var moveY = Math.trunc(moveYSum);
|
||||
if (Math.abs(moveX) >= 1 || Math.abs(moveY) >= 1) {
|
||||
moveXSum -= moveX;
|
||||
moveYSum -= moveY;
|
||||
idleUpdates = 0;
|
||||
ws.send("PointerMove " + moveX + " " + moveY);
|
||||
}
|
||||
var scrollX = Math.trunc(scrollXSum);
|
||||
var scrollY = Math.trunc(scrollYSum);
|
||||
if (Math.abs(scrollX) >= 1 || Math.abs(scrollY) >= 1) {
|
||||
scrollXSum -= scrollX;
|
||||
scrollYSum -= scrollY;
|
||||
idleUpdates = 0;
|
||||
ws.send("PointerScroll " + scrollX + " " + scrollY);
|
||||
}
|
||||
if (idleUpdates >= MAX_IDLE_UPDATES) {
|
||||
clearInterval(updateTimer);
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function handleStart(evt) {
|
||||
if (ongoingTouches.length == 0) {
|
||||
touchStart = evt.timeStamp;
|
||||
touchMoved = false;
|
||||
touchReleasedCount = 0;
|
||||
dragging = false;
|
||||
}
|
||||
var touches = evt.changedTouches;
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
if (touches[i].target !== pad && touches[i].target !== padlabel) {
|
||||
continue;
|
||||
}
|
||||
evt.preventDefault();
|
||||
ongoingTouches.push(copyTouch(touches[i], evt.timeStamp));
|
||||
touchLastEnd = 0;
|
||||
if (!dragging) {
|
||||
moveXSum = Math.trunc(moveXSum);
|
||||
moveYSum = Math.trunc(moveYSum);
|
||||
}
|
||||
scrollXSum = Math.trunc(scrollXSum);
|
||||
scrollYSum = Math.trunc(scrollYSum);
|
||||
if (draggingTimeout !== null) {
|
||||
clearTimeout(draggingTimeout);
|
||||
draggingTimeout = null;
|
||||
dragging = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnd(evt) {
|
||||
var touches = evt.changedTouches;
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
var idx = ongoingTouchIndexById(touches[i].identifier);
|
||||
if (idx < 0) {
|
||||
continue;
|
||||
}
|
||||
ongoingTouches.splice(idx, 1);
|
||||
touchReleasedCount++;
|
||||
touchLastEnd = evt.timeStamp;
|
||||
}
|
||||
if (touchReleasedCount > TOUCH_MOVE_THRESHOLD.length) {
|
||||
touchMoved = true;
|
||||
}
|
||||
if (ongoingTouches.length == 0 && touchReleasedCount >= 1 &&
|
||||
dragging) {
|
||||
ws.send("PointerButton 1 0");
|
||||
}
|
||||
if (ongoingTouches.length == 0 && touchReleasedCount >= 1 &&
|
||||
!touchMoved && evt.timeStamp - touchStart < TOUCH_TIMEOUT) {
|
||||
var button = 0;
|
||||
if (touchReleasedCount == 1) {
|
||||
button = 1;
|
||||
} else if (touchReleasedCount == 2) {
|
||||
button = 3;
|
||||
}else if (touchReleasedCount == 3) {
|
||||
button = 2;
|
||||
}
|
||||
ws.send("PointerButton " + button + " 1");
|
||||
if (button == 1) {
|
||||
draggingTimeout = setTimeout(onDraggingTimeout, TOUCH_TIMEOUT);
|
||||
} else {
|
||||
ws.send("PointerButton " + button + " 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel(evt) {
|
||||
var touches = evt.changedTouches;
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
var idx = ongoingTouchIndexById(touches[i].identifier);
|
||||
if (idx < 0) {
|
||||
continue;
|
||||
}
|
||||
ongoingTouches.splice(idx, 1);
|
||||
touchReleasedCount++;
|
||||
touchLastEnd = evt.timeStamp;
|
||||
touchMoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMove(evt) {
|
||||
var sumX = 0;
|
||||
var sumY = 0;
|
||||
var touches = evt.changedTouches;
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
var idx = ongoingTouchIndexById(touches[i].identifier);
|
||||
if (idx < 0) {
|
||||
continue;
|
||||
}
|
||||
var dist = Math.sqrt(Math.pow(touches[i].pageX - ongoingTouches[idx].pageXStart, 2) + Math.pow(touches[i].pageY - ongoingTouches[idx].pageYStart, 2));
|
||||
if (ongoingTouches.length > TOUCH_MOVE_THRESHOLD.length || dist > TOUCH_MOVE_THRESHOLD[ongoingTouches.length - 1]) {
|
||||
touchMoved = true;
|
||||
}
|
||||
var dx = touches[i].pageX - ongoingTouches[idx].pageX;
|
||||
var dy = touches[i].pageY - ongoingTouches[idx].pageY;
|
||||
var timeDelta = evt.timeStamp - ongoingTouches[idx].timeStamp;
|
||||
sumX += dx * calculatePointerAccelerationMult(Math.abs(dx) / timeDelta * 1000);
|
||||
sumY += dy * calculatePointerAccelerationMult(Math.abs(dy) / timeDelta * 1000);
|
||||
ongoingTouches[idx].pageX = touches[i].pageX;
|
||||
ongoingTouches[idx].pageY = touches[i].pageY;
|
||||
ongoingTouches[idx].timeStamp = evt.timeStamp;
|
||||
}
|
||||
if (touchMoved && evt.timeStamp - touchLastEnd >= TOUCH_TIMEOUT) {
|
||||
if (ongoingTouches.length == 1 || dragging) {
|
||||
moveXSum += sumX * MOVE_MULT;
|
||||
moveYSum += sumY * MOVE_MULT;
|
||||
} else if (ongoingTouches.length == 2) {
|
||||
scrollXSum -= sumX * SCROLL_MULT;
|
||||
scrollYSum -= sumY * SCROLL_MULT;
|
||||
}
|
||||
updateMoveAndScroll();
|
||||
}
|
||||
}
|
||||
|
||||
function challengeResponse(message) {
|
||||
var shaObj = new jsSHA("SHA-256", "TEXT");
|
||||
shaObj.setHMACKey(message, "TEXT");
|
||||
shaObj.update(window.location.hash.substr(1));
|
||||
return btoa(shaObj.getHMAC("BYTES"));
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
var authenticated = false;
|
||||
var opening = document.getElementById("opening");
|
||||
var closed = document.getElementById("closed");
|
||||
pad = document.getElementById("pad");
|
||||
padlabel = document.getElementById("padlabel");
|
||||
var keyboard = document.getElementById("keyboard");
|
||||
var fullscreenbutton = document.getElementById("fullscreenbutton");
|
||||
var text = document.getElementById("text");
|
||||
closed.style.display = "none";
|
||||
pad.style.display = "none";
|
||||
keyboard.style.display = "none";
|
||||
text.value = "";
|
||||
|
||||
ws = new WebSocket("ws://" + location.hostname +
|
||||
(location.port ? ":" + location.port : "") +
|
||||
"/ws");
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
if (authenticated) {
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
authenticated = true;
|
||||
ws.send(challengeResponse(event.data));
|
||||
opening.style.display = "none";
|
||||
if (history.state == "keyboard") {
|
||||
keyboard.style.display = "flex";
|
||||
text.focus();
|
||||
} else {
|
||||
pad.style.display = "flex";
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
if (fullscreenElement()) {
|
||||
exitFullscreen();
|
||||
}
|
||||
opening.style.display = "none";
|
||||
pad.style.display = "none";
|
||||
keyboard.style.display = "none"
|
||||
closed.style.display = "flex";
|
||||
};
|
||||
|
||||
document.getElementById("keyboardbutton").addEventListener("click",
|
||||
function(e) {
|
||||
if (fullscreenElement()) {
|
||||
exitFullscreen();
|
||||
}
|
||||
pad.style.display = "none";
|
||||
keyboard.style.display = "flex";
|
||||
text.focus();
|
||||
history.pushState("keyboard", "Remote Keyboard");
|
||||
});
|
||||
if (!fullscreenEnabled()) {
|
||||
fullscreenbutton.style.display = "none";
|
||||
}
|
||||
fullscreenbutton.addEventListener("click", function(e) {
|
||||
if (fullscreenElement()) {
|
||||
exitFullscreen();
|
||||
} else {
|
||||
requestFullscreen(pad);
|
||||
}
|
||||
});
|
||||
document.getElementById("sendbutton").addEventListener("click",
|
||||
function(e) {
|
||||
if (text.value != "") {
|
||||
ws.send("KeyboardText " + text.value);
|
||||
text.value = "";
|
||||
}
|
||||
window.history.back();
|
||||
});
|
||||
window.onpopstate = function(event) {
|
||||
if (keyboard.style.display == "flex") {
|
||||
pad.style.display = "flex";
|
||||
keyboard.style.display = "none";
|
||||
} else {
|
||||
window.history.back();
|
||||
}
|
||||
};
|
||||
pad.addEventListener("touchstart", handleStart, false);
|
||||
pad.addEventListener("touchend", handleEnd, false);
|
||||
pad.addEventListener("touchcancel", handleCancel, false);
|
||||
pad.addEventListener("touchmove", handleMove, false);
|
||||
}, false);
|
||||
BIN
webdata/icon.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
32
webdata/index.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width initial-scale=1, maximum-scale=1, user-scalable=0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<script src="fn.js"></script>
|
||||
<script src="sha256.js"></script>
|
||||
<title>Remote Touchpad</title>
|
||||
<link href="main.css" media="screen" rel="stylesheet" />
|
||||
<link href="icon.png" type="image/png" rel="shortcut icon" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="opening">
|
||||
<p>Connecting...</p>
|
||||
</div>
|
||||
<div id="closed">
|
||||
<p>Disconnected</p>
|
||||
</div>
|
||||
<div id="pad">
|
||||
<p id="padlabel">Touchpad</p>
|
||||
<div class="buttons">
|
||||
<button id="fullscreenbutton">▢</button>
|
||||
<button id="keyboardbutton">⌨</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="keyboard">
|
||||
<textarea id="text"></textarea>
|
||||
<button id="sendbutton">➣</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
99
webdata/main.css
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
html, body, #pad, #keyboard, #opening, #closed {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans;
|
||||
}
|
||||
|
||||
#pad, #sendbutton, #opening, #closed {
|
||||
background-color: #404040 !important;
|
||||
background-image: url(tex.png);
|
||||
}
|
||||
|
||||
#opening, #closed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2em;
|
||||
color: white;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#pad {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#padlabel {
|
||||
color: #424242;
|
||||
text-shadow:
|
||||
-1px -1px 0 #383838,
|
||||
1px -1px 0 #383838,
|
||||
-1px 1px 0 #383838,
|
||||
1px 1px 0 #383838;
|
||||
font-weight: bold;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
text-align: center;
|
||||
font-size: 4em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#fullscreenbutton, #keyboardbutton, #sendbutton {
|
||||
background-color: transparent;
|
||||
border: 1px solid black;
|
||||
border-bottom: none;
|
||||
height: 1.5em;
|
||||
width: 2em;
|
||||
font-size: 2em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#fullscreenbutton {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
#keyboardbutton {
|
||||
border-right: none;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#keyboard {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#text {
|
||||
flex: 2;
|
||||
border: none;
|
||||
resize: none;
|
||||
font-size: 2em;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#sendbutton {
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-aspect-ratio: 4/3) {
|
||||
#keyboard {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#sendbutton {
|
||||
border-left: none;
|
||||
border-top: 1px solid black;
|
||||
height: 1.5em;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
27
webdata/sha256.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
A JavaScript implementation of the SHA family of hashes, as
|
||||
defined in FIPS PUB 180-4 and FIPS PUB 202, as well as the corresponding
|
||||
HMAC implementation as defined in FIPS PUB 198a
|
||||
|
||||
Copyright 2008-2018 Brian Turek, 1998-2009 Paul Johnston & Contributors
|
||||
Distributed under the BSD License
|
||||
See http://caligatio.github.com/jsSHA/ for more information
|
||||
*/
|
||||
'use strict';(function(I){function w(c,a,d){var l=0,b=[],g=0,f,n,k,e,h,q,y,p,m=!1,t=[],r=[],u,z=!1;d=d||{};f=d.encoding||"UTF8";u=d.numRounds||1;if(u!==parseInt(u,10)||1>u)throw Error("numRounds must a integer >= 1");if(0===c.lastIndexOf("SHA-",0))if(q=function(b,a){return A(b,a,c)},y=function(b,a,l,f){var g,e;if("SHA-224"===c||"SHA-256"===c)g=(a+65>>>9<<4)+15,e=16;else throw Error("Unexpected error in SHA-2 implementation");for(;b.length<=g;)b.push(0);b[a>>>5]|=128<<24-a%32;a=a+l;b[g]=a&4294967295;
|
||||
b[g-1]=a/4294967296|0;l=b.length;for(a=0;a<l;a+=e)f=A(b.slice(a,a+e),f,c);if("SHA-224"===c)b=[f[0],f[1],f[2],f[3],f[4],f[5],f[6]];else if("SHA-256"===c)b=f;else throw Error("Unexpected error in SHA-2 implementation");return b},p=function(b){return b.slice()},"SHA-224"===c)h=512,e=224;else if("SHA-256"===c)h=512,e=256;else throw Error("Chosen SHA variant is not supported");else throw Error("Chosen SHA variant is not supported");k=B(a,f);n=x(c);this.setHMACKey=function(b,a,g){var e;if(!0===m)throw Error("HMAC key already set");
|
||||
if(!0===z)throw Error("Cannot set HMAC key after calling update");f=(g||{}).encoding||"UTF8";a=B(a,f)(b);b=a.binLen;a=a.value;e=h>>>3;g=e/4-1;if(e<b/8){for(a=y(a,b,0,x(c));a.length<=g;)a.push(0);a[g]&=4294967040}else if(e>b/8){for(;a.length<=g;)a.push(0);a[g]&=4294967040}for(b=0;b<=g;b+=1)t[b]=a[b]^909522486,r[b]=a[b]^1549556828;n=q(t,n);l=h;m=!0};this.update=function(a){var c,f,e,d=0,p=h>>>5;c=k(a,b,g);a=c.binLen;f=c.value;c=a>>>5;for(e=0;e<c;e+=p)d+h<=a&&(n=q(f.slice(e,e+p),n),d+=h);l+=d;b=f.slice(d>>>
|
||||
5);g=a%h;z=!0};this.getHash=function(a,f){var d,h,k,q;if(!0===m)throw Error("Cannot call getHash after setting HMAC key");k=C(f);switch(a){case "HEX":d=function(a){return D(a,e,k)};break;case "B64":d=function(a){return E(a,e,k)};break;case "BYTES":d=function(a){return F(a,e)};break;case "ARRAYBUFFER":try{h=new ArrayBuffer(0)}catch(v){throw Error("ARRAYBUFFER not supported by this environment");}d=function(a){return G(a,e)};break;default:throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER");
|
||||
}q=y(b.slice(),g,l,p(n));for(h=1;h<u;h+=1)q=y(q,e,0,x(c));return d(q)};this.getHMAC=function(a,f){var d,k,t,u;if(!1===m)throw Error("Cannot call getHMAC without first setting HMAC key");t=C(f);switch(a){case "HEX":d=function(a){return D(a,e,t)};break;case "B64":d=function(a){return E(a,e,t)};break;case "BYTES":d=function(a){return F(a,e)};break;case "ARRAYBUFFER":try{d=new ArrayBuffer(0)}catch(v){throw Error("ARRAYBUFFER not supported by this environment");}d=function(a){return G(a,e)};break;default:throw Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER");
|
||||
}k=y(b.slice(),g,l,p(n));u=q(r,x(c));u=y(k,e,h,u);return d(u)}}function m(){}function D(c,a,d){var l="";a/=8;var b,g;for(b=0;b<a;b+=1)g=c[b>>>2]>>>8*(3+b%4*-1),l+="0123456789abcdef".charAt(g>>>4&15)+"0123456789abcdef".charAt(g&15);return d.outputUpper?l.toUpperCase():l}function E(c,a,d){var l="",b=a/8,g,f,n;for(g=0;g<b;g+=3)for(f=g+1<b?c[g+1>>>2]:0,n=g+2<b?c[g+2>>>2]:0,n=(c[g>>>2]>>>8*(3+g%4*-1)&255)<<16|(f>>>8*(3+(g+1)%4*-1)&255)<<8|n>>>8*(3+(g+2)%4*-1)&255,f=0;4>f;f+=1)8*g+6*f<=a?l+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(n>>>
|
||||
6*(3-f)&63):l+=d.b64Pad;return l}function F(c,a){var d="",l=a/8,b,g;for(b=0;b<l;b+=1)g=c[b>>>2]>>>8*(3+b%4*-1)&255,d+=String.fromCharCode(g);return d}function G(c,a){var d=a/8,l,b=new ArrayBuffer(d),g;g=new Uint8Array(b);for(l=0;l<d;l+=1)g[l]=c[l>>>2]>>>8*(3+l%4*-1)&255;return b}function C(c){var a={outputUpper:!1,b64Pad:"=",shakeLen:-1};c=c||{};a.outputUpper=c.outputUpper||!1;!0===c.hasOwnProperty("b64Pad")&&(a.b64Pad=c.b64Pad);if("boolean"!==typeof a.outputUpper)throw Error("Invalid outputUpper formatting option");
|
||||
if("string"!==typeof a.b64Pad)throw Error("Invalid b64Pad formatting option");return a}function B(c,a){var d;switch(a){case "UTF8":case "UTF16BE":case "UTF16LE":break;default:throw Error("encoding must be UTF8, UTF16BE, or UTF16LE");}switch(c){case "HEX":d=function(a,b,c){var f=a.length,d,k,e,h,q;if(0!==f%2)throw Error("String of HEX type must be in byte increments");b=b||[0];c=c||0;q=c>>>3;for(d=0;d<f;d+=2){k=parseInt(a.substr(d,2),16);if(isNaN(k))throw Error("String of HEX type contains invalid characters");
|
||||
h=(d>>>1)+q;for(e=h>>>2;b.length<=e;)b.push(0);b[e]|=k<<8*(3+h%4*-1)}return{value:b,binLen:4*f+c}};break;case "TEXT":d=function(c,b,d){var f,n,k=0,e,h,q,m,p,r;b=b||[0];d=d||0;q=d>>>3;if("UTF8"===a)for(r=3,e=0;e<c.length;e+=1)for(f=c.charCodeAt(e),n=[],128>f?n.push(f):2048>f?(n.push(192|f>>>6),n.push(128|f&63)):55296>f||57344<=f?n.push(224|f>>>12,128|f>>>6&63,128|f&63):(e+=1,f=65536+((f&1023)<<10|c.charCodeAt(e)&1023),n.push(240|f>>>18,128|f>>>12&63,128|f>>>6&63,128|f&63)),h=0;h<n.length;h+=1){p=k+
|
||||
q;for(m=p>>>2;b.length<=m;)b.push(0);b[m]|=n[h]<<8*(r+p%4*-1);k+=1}else if("UTF16BE"===a||"UTF16LE"===a)for(r=2,n="UTF16LE"===a&&!0||"UTF16LE"!==a&&!1,e=0;e<c.length;e+=1){f=c.charCodeAt(e);!0===n&&(h=f&255,f=h<<8|f>>>8);p=k+q;for(m=p>>>2;b.length<=m;)b.push(0);b[m]|=f<<8*(r+p%4*-1);k+=2}return{value:b,binLen:8*k+d}};break;case "B64":d=function(a,b,c){var f=0,d,k,e,h,q,m,p;if(-1===a.search(/^[a-zA-Z0-9=+\/]+$/))throw Error("Invalid character in base-64 string");k=a.indexOf("=");a=a.replace(/\=/g,
|
||||
"");if(-1!==k&&k<a.length)throw Error("Invalid '=' found in base-64 string");b=b||[0];c=c||0;m=c>>>3;for(k=0;k<a.length;k+=4){q=a.substr(k,4);for(e=h=0;e<q.length;e+=1)d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(q[e]),h|=d<<18-6*e;for(e=0;e<q.length-1;e+=1){p=f+m;for(d=p>>>2;b.length<=d;)b.push(0);b[d]|=(h>>>16-8*e&255)<<8*(3+p%4*-1);f+=1}}return{value:b,binLen:8*f+c}};break;case "BYTES":d=function(a,b,c){var d,n,k,e,h;b=b||[0];c=c||0;k=c>>>3;for(n=0;n<a.length;n+=
|
||||
1)d=a.charCodeAt(n),h=n+k,e=h>>>2,b.length<=e&&b.push(0),b[e]|=d<<8*(3+h%4*-1);return{value:b,binLen:8*a.length+c}};break;case "ARRAYBUFFER":try{d=new ArrayBuffer(0)}catch(l){throw Error("ARRAYBUFFER not supported by this environment");}d=function(a,b,c){var d,n,k,e,h;b=b||[0];c=c||0;n=c>>>3;h=new Uint8Array(a);for(d=0;d<a.byteLength;d+=1)e=d+n,k=e>>>2,b.length<=k&&b.push(0),b[k]|=h[d]<<8*(3+e%4*-1);return{value:b,binLen:8*a.byteLength+c}};break;default:throw Error("format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER");
|
||||
}return d}function r(c,a){return c>>>a|c<<32-a}function J(c,a,d){return c&a^~c&d}function K(c,a,d){return c&a^c&d^a&d}function L(c){return r(c,2)^r(c,13)^r(c,22)}function M(c){return r(c,6)^r(c,11)^r(c,25)}function N(c){return r(c,7)^r(c,18)^c>>>3}function O(c){return r(c,17)^r(c,19)^c>>>10}function P(c,a){var d=(c&65535)+(a&65535);return((c>>>16)+(a>>>16)+(d>>>16)&65535)<<16|d&65535}function Q(c,a,d,l){var b=(c&65535)+(a&65535)+(d&65535)+(l&65535);return((c>>>16)+(a>>>16)+(d>>>16)+(l>>>16)+(b>>>
|
||||
16)&65535)<<16|b&65535}function R(c,a,d,l,b){var g=(c&65535)+(a&65535)+(d&65535)+(l&65535)+(b&65535);return((c>>>16)+(a>>>16)+(d>>>16)+(l>>>16)+(b>>>16)+(g>>>16)&65535)<<16|g&65535}function x(c){var a=[],d;if(0===c.lastIndexOf("SHA-",0))switch(a=[3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428],d=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],c){case "SHA-224":break;case "SHA-256":a=d;break;case "SHA-384":a=[new m,new m,
|
||||
new m,new m,new m,new m,new m,new m];break;case "SHA-512":a=[new m,new m,new m,new m,new m,new m,new m,new m];break;default:throw Error("Unknown SHA variant");}else throw Error("No SHA variants supported");return a}function A(c,a,d){var l,b,g,f,n,k,e,h,m,r,p,w,t,x,u,z,A,B,C,D,E,F,v=[],G;if("SHA-224"===d||"SHA-256"===d)r=64,w=1,F=Number,t=P,x=Q,u=R,z=N,A=O,B=L,C=M,E=K,D=J,G=H;else throw Error("Unexpected error in SHA-2 implementation");d=a[0];l=a[1];b=a[2];g=a[3];f=a[4];n=a[5];k=a[6];e=a[7];for(p=
|
||||
0;p<r;p+=1)16>p?(m=p*w,h=c.length<=m?0:c[m],m=c.length<=m+1?0:c[m+1],v[p]=new F(h,m)):v[p]=x(A(v[p-2]),v[p-7],z(v[p-15]),v[p-16]),h=u(e,C(f),D(f,n,k),G[p],v[p]),m=t(B(d),E(d,l,b)),e=k,k=n,n=f,f=t(g,h),g=b,b=l,l=d,d=t(h,m);a[0]=t(d,a[0]);a[1]=t(l,a[1]);a[2]=t(b,a[2]);a[3]=t(g,a[3]);a[4]=t(f,a[4]);a[5]=t(n,a[5]);a[6]=t(k,a[6]);a[7]=t(e,a[7]);return a}var H;H=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,
|
||||
2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,
|
||||
2756734187,3204031479,3329325298];"function"===typeof define&&define.amd?define(function(){return w}):"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(module.exports=w),exports=w):I.jsSHA=w})(this);
|
||||
BIN
webdata/tex.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |