chore: clean up repo + readme

This commit is contained in:
2024-01-29 14:07:45 +01:00
parent 28bec5ebb8
commit dfee9fe461
122 changed files with 61 additions and 11991 deletions

7
.gitignore vendored
View File

@@ -1,9 +1,8 @@
!.gitkeep
tmp/
src/tmp
node_modules/
obj/**/*
out/**/*
obj/
out/
!.gitkeep

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "git/wsserver"]
path = git/wsserver
url = https://github.com/theldus/wsserver

View File

@@ -1,24 +1,3 @@
<!---
To update:
project.
master - The master org or user of the repo
linter - The linter used for the project
name - Project name
lang - The (primary) programming language used
logo - Project logo
contact - All ways to contact for any inqueries
reviewers - A list of users to auto-assign to issues and features
info.
toc - Table of Contents of README
desc - Small project description
badges - Any extra badges
setup.
prerequisites - The prerequisites needed to run the project
install - Command for initial installation
test - Command for running tests
tree.
parts - All individual parts of the project
--->
<img src="https://avatars.githubusercontent.com/u/29184334?v=4" align="left" width="192px" height="192px"/>
<img align="left" width="0" height="192px" hspace="10"/>
@@ -26,15 +5,55 @@ To update:
[![MIT License](https://img.shields.io/badge/license-MIT-007EC7.svg?style=flat-square)](/LICENSE)
A simple websocket server written in C for InHolland.
<br><br>
<!---
Example table of contents:
* header
* sub header
--->
<!-- ## Table of contents
{{info.toc}} -->
## Table of Contents
* Introduction
* Libraries
* Data Structure
## Introduction
This websocket server parses JSON data sent by the client
in order to control the state of GPIO pins by writing them to a SQL database.
## Libraries
[argp](https://www.gnu.org/software/libc/manual/html_node/Argp.html) is used
for simplification of command line argument parsing.
[libtap](https://github.com/zorgnax/libtap) is used for unit testing, all tests
can be found inside the [/tests](/tests) directory.
[wsServer](https://github.com/Theldus/wsServer) is used as socket server, writing
a custom socket library was abundant for the course followed.
[json-c](https://github.com/json-c/json-c) is used to cast incoming JSON objects
into data envelopes.
[libmysqlclient](https://dev.mysql.com/downloads/c-api/) is used for a connection
to the SQL database.
## Data Structure
A request from the client would look as follows:
```json
{
"auth": <authentication:str>
"payload": {
"sensorId": <sensor.id:str>
"sensorData": <sensor.data:str>
}
}
```
After the request has been parsed, it will be processed by a handler which will
in turn give a status code / data struct. This can be one of:
```c
enum RESPONSE_CODES {
SUCCESS,
MISSING_JSON,
MISSING_PAYLOAD,
MISSING_AUTH
};
[ { "sensorId": <sensor.id:str>, "state": <sensor.state:str> }, ... ]
```

View File

@@ -1,28 +0,0 @@
---
BasedOnStyle: Microsoft
AlignAfterOpenBracket: DontAlign
AlignConsecutiveMacros: 'true'
AlignConsecutiveAssignments: 'false'
AlignEscapedNewlines: Left
AlignTrailingComments: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: 'false'
BinPackParameters: 'false'
ColumnLimit: '85'
IndentCaseLabels: 'true'
IndentWidth: '4'
Language: Cpp
PointerAlignment: Right
SpaceAfterCStyleCast: 'false'
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: 'false'
SpacesInCStyleCastParentheses: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
TabWidth: '4'
UseTab: ForContinuationAndIndentation
...

View File

@@ -1,27 +0,0 @@
# Copyright (C) 2016-2020 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# Ignore object files, executables and the library.
*.o
*.a
ws_file
toyws_test
build/
doc/doxygen/html
doc/doxygen/xml
tests/wsserver_autobahn/report
tests/fuzzy/out/
examples/echo/echo
examples/ping/ping

View File

@@ -1,66 +0,0 @@
#
# Copyright (C) 2021 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
os: linux
dist: focal
language: c
env:
- CTEST_OUTPUT_ON_FAILURE=1
jobs:
include:
- os: linux
services: docker
name: "Linux (AMD64) Build & Test"
script:
- >
mkdir build &&
cd build &&
cmake ..
-DCMAKE_BUILD_TYPE=Release
-DENABLE_WSSERVER_TEST=On &&
make -j$(nproc) &&
make test
- os: linux
services: docker
name: "Windows (via Wine) (AMD64) Build & Test"
before_script: sudo apt-get install mingw-w64 wine64 -y
script:
- >
wget https://git.io/JcAzE -O mingw-w64-x86_64.cmake &&
mkdir build &&
cd build &&
cmake ..
-DCMAKE_TOOLCHAIN_FILE=../mingw-w64-x86_64.cmake
-DCMAKE_BUILD_TYPE=Release
-DENABLE_WSSERVER_TEST=On &&
make -j$(nproc) &&
make test
- os: osx
name: "macOS (AMD64) (Build)"
- os: freebsd
name: "FreeBSD (AMD64) (Build)"
script:
- >
mkdir build && cd build &&
cmake ..
-DCMAKE_BUILD_TYPE=Release &&
make -j2

View File

@@ -1,60 +0,0 @@
# Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
cmake_minimum_required(VERSION 3.5.2)
project(ws C)
set(CMAKE_C_STANDARD 99)
include_directories(include)
add_library(ws
src/ws.c
src/base64.c
src/sha1.c
src/handshake.c
src/utf8.c
)
if(WIN32)
target_link_libraries(ws pthread ws2_32 -static)
else()
target_link_libraries(ws pthread)
endif(WIN32)
add_executable(toyws_test
extra/toyws/tws_test.c
extra/toyws/toyws.c)
if(WIN32)
target_link_libraries(toyws_test ws2_32 -static)
endif(WIN32)
# Examples files
add_subdirectory(examples)
option(ENABLE_WSSERVER_TEST "Enable wsServer testing (requires Autobahn)" OFF)
if(ENABLE_WSSERVER_TEST)
enable_testing()
# Disable verbose output of echo
target_compile_definitions(echo PRIVATE DISABLE_VERBOSE)
add_subdirectory(tests)
endif(ENABLE_WSSERVER_TEST)
option(VALIDATE_UTF8 "Enable UTF-8 validation (default ON)" ON)
if(VALIDATE_UTF8)
target_compile_definitions(ws PRIVATE VALIDATE_UTF8)
endif(VALIDATE_UTF8)

View File

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

View File

@@ -1,169 +0,0 @@
# Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
CC ?= gcc
AR = ar
INCLUDE = $(CURDIR)/include
SRC = $(CURDIR)/src
CFLAGS += -Wall -Wextra -O2
CFLAGS += -I $(INCLUDE) -std=c99 -pedantic
ARFLAGS = cru
LIB = libws.a
MCSS_DIR ?= /usr/bin/
MANPAGES = $(CURDIR)/doc/man/man3
AFL_FUZZ ?= no
VERBOSE_EXAMPLES ?= yes
VALIDATE_UTF8 ?= yes
# Prefix
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
# Detect machine type
MACHINE := $(shell uname -m)
ifeq ($(MACHINE), x86_64)
LIBDIR = $(PREFIX)/lib64
else
LIBDIR = $(PREFIX)/lib
endif
# Check if AFL fuzzing enabled
ifeq ($(AFL_FUZZ), yes)
CC = afl-gcc
CFLAGS := $(filter-out -O2,$(CFLAGS))
CFLAGS += -DVERBOSE_MODE -DAFL_FUZZ -g
$(info [+] AFL Fuzzing build enabled)
endif
# Check if UTF-8 validation is enabled
ifeq ($(VALIDATE_UTF8), yes)
CFLAGS += -DVALIDATE_UTF8
endif
# Source
C_SRC = $(SRC)/base64.c \
$(SRC)/handshake.c \
$(SRC)/sha1.c \
$(SRC)/utf8.c \
$(SRC)/ws.c
OBJ = $(C_SRC:.c=.o)
# Conflicts
.PHONY: doc fuzzy
# Paths
INCDIR = $(PREFIX)/include
BINDIR = $(PREFIX)/bin
MANDIR = $(PREFIX)/man
PKGDIR = $(LIBDIR)/pkgconfig
# Extra paths
TOYWS = $(CURDIR)/extra/toyws
# General objects
%.o: %.c
$(CC) $< $(CFLAGS) -c -o $@
# All
ifeq ($(AFL_FUZZ),no)
all: libws.a examples
else
all: libws.a fuzzy
endif
# Library
libws.a: $(OBJ)
$(AR) $(ARFLAGS) $(LIB) $^
# Examples
examples: libws.a
$(MAKE) -C examples/
# Autobahn tests
tests: examples
$(MAKE) all -C tests/ VERBOSE_EXAMPLES="$(VERBOSE_EXAMPLES)"
tests_check:
$(MAKE) check_results -C tests/
# Fuzzing tests
fuzzy: libws.a
$(MAKE) -C tests/fuzzy
# ToyWS client
toyws_test: $(TOYWS)/tws_test.o $(TOYWS)/toyws.o
$(CC) $^ $(CFLAGS) -I $(TOYWS) -o $@
# Install rules
install: libws.a wsserver.pc
@#Library
install -d $(DESTDIR)$(LIBDIR)
install -m 644 $(LIB) $(DESTDIR)$(LIBDIR)
@#Headers
install -d $(DESTDIR)$(INCDIR)/wsserver
install -m 644 $(INCLUDE)/*.h $(DESTDIR)$(INCDIR)/wsserver
@#Manpages
install -d $(DESTDIR)$(MANDIR)/man3
install -m 0644 $(MANPAGES)/*.3 $(DESTDIR)$(MANDIR)/man3/
# Uninstall rules
uninstall:
rm -f $(DESTDIR)$(LIBDIR)/$(LIB)
rm -rf $(DESTDIR)$(INCDIR)/wsserver
rm -f $(DESTDIR)$(MANDIR)/man3/ws_getaddress.3
rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe.3
rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe_bin.3
rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe_txt.3
rm -f $(DESTDIR)$(MANDIR)/man3/ws_socket.3
rm -f $(DESTDIR)$(MANDIR)/man3/ws_close_client.3
rm -f $(DESTDIR)$(MANDIR)/man3/ws_get_state.3
rm -f $(DESTDIR)$(PKGDIR)/wsserver.pc
# Generate wsserver.pc
wsserver.pc:
@install -d $(DESTDIR)$(PKGDIR)
@echo 'prefix='$(DESTDIR)$(PREFIX) > $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'libdir='$(DESTDIR)$(LIBDIR) >> $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'includedir=$${prefix}/include' >> $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'Name: wsServer' >> $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'Description: Tiny WebSocket Server Library' >> $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'Version: 1.0' >> $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'Libs: -L$${libdir} -lws -pthread' >> $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'Libs.private:' >> $(DESTDIR)$(PKGDIR)/wsserver.pc
@echo 'Cflags: -I$${includedir}/wsserver' >> $(DESTDIR)$(PKGDIR)/wsserver.pc
# Documentation, requires Doxygen and m.css
# -> https://mcss.mosra.cz/documentation/doxygen/
#
# If for some reason m.css usage is not possible,
# change the following lines in doxy.conf to:
# GENERATE_HTML = no -> GENERATE_HTML = yes
# GENERATE_XML = yes -> GENERATE_XML = no
#
doc:
@echo "Generating docs..."
@doxygen doc/doxygen/doxy.conf
@$(MCSS_DIR)/doxygen.py doc/doxygen/Doxyfile-mcss --no-doxygen\
--templates doc/doxygen/templates/
# Clean
clean:
@rm -f $(OBJ)
@rm -f $(LIB)
@rm -f $(TOYWS)/toyws.o $(TOYWS)/tws_test.o toyws_test
@$(MAKE) clean -C examples/
@$(MAKE) clean -C tests/
@$(MAKE) clean -C tests/fuzzy

View File

@@ -1,188 +0,0 @@
# wsServer
[![License: GPL v3](https://img.shields.io/badge/license-GPLv3-blue)](https://opensource.org/licenses/GPL-3.0)
[![Build Status for Windows, Linux, macOS and FreeBSD](https://app.travis-ci.com/Theldus/wsServer.svg?branch=master)](https://app.travis-ci.com/Theldus/wsServer)
wsServer - a very tiny WebSocket server library written in C
## Library
wsServer is a tiny, lightweight WebSocket server library written in C that intends
to be easy to use, fast, hackable, and compliant to the
[RFC 6455](https://tools.ietf.org/html/rfc6455).
The main features are:
- Send/Receive Text and Binary messages
- PING/PONG frames
- Opening/Closing handshakes
- Event based (onmessage, onopen, onclose)
- Portability: Works fine on Windows, Linux (Android included), macOS and FreeBSD
See Autobahn [report](https://theldus.github.io/wsServer/autobahn) and the
[docs](doc/AUTOBAHN.md) for an 'in-depth' analysis.
## Building
wsServer only requires a C99-compatible compiler (such as GCC, Clang, TCC and others) and
no external libraries.
### Make
The preferred way to build wsServer on Linux environments:
```bash
git clone https://github.com/Theldus/wsServer
cd wsServer/
make
# Optionally, a user can also install wsServer into the system,
# either on default paths or by providing PREFIX or DESTDIR env
# vars to the Makefile.
make install # Or make install DESTDIR=/my/folder/
```
### CMake
CMake enables the user to easily build wsServer in others environments other than Linux
and also allows the use of an IDE to build the project automatically. If that's
your case:
```bash
git clone https://github.com/Theldus/wsServer
cd wsServer/
mkdir build && cd build/
cmake ..
make
./examples/echo/echo # Waiting for incoming connections...
```
### Windows support
Windows has native support via MinGW, toolchain setup and build steps are detailed
[here](https://github.com/Theldus/wsServer/blob/master/doc/BUILD_WINDOWS.md).
## Why to complicate if things can be simple?
wsServer abstracts the idea of sockets and you only need to deal with three
types of events defined:
```c
/* New client. */
void onopen(ws_cli_conn_t *client);
/* Client disconnected. */
void onclose(ws_cli_conn_t *client);
/* Client sent a text message. */
void onmessage(ws_cli_conn_t *client, const unsigned char *msg,
uint64_t size, int type);
```
this is all you need to worry about, nothing to think about return values in socket,
accepting connections, and so on.
As a gift, each client is handled in a separate thread, so you will not have to
worry about it.
### A complete example
More examples, including the respective html files, can be found in examples/
folder, ;-).
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>
/**
* @brief This function is called whenever a new connection is opened.
* @param client Client connection.
*/
void onopen(ws_cli_conn_t *client)
{
char *cli;
cli = ws_getaddress(client);
printf("Connection opened, addr: %s\n", cli);
}
/**
* @brief This function is called whenever a connection is closed.
* @param client Client connection.
*/
void onclose(ws_cli_conn_t *client)
{
char *cli;
cli = ws_getaddress(client);
printf("Connection closed, addr: %s\n", cli);
}
/**
* @brief Message events goes here.
* @param client Client connection.
* @param msg Message content.
* @param size Message size.
* @param type Message type.
*/
void onmessage(ws_cli_conn_t *client,
const unsigned char *msg, uint64_t size, int type)
{
char *cli;
cli = ws_getaddress(client);
printf("I receive a message: %s (%zu), from: %s\n", msg,
size, cli);
sleep(2);
ws_sendframe_txt(client, "hello");
sleep(2);
ws_sendframe_txt(client, "world");
}
int main(void)
{
/* Register events. */
struct ws_events evs;
evs.onopen = &onopen;
evs.onclose = &onclose;
evs.onmessage = &onmessage;
/*
* Main loop, this function never* returns.
*
* *If the third argument is != 0, a new thread is created
* to handle new connections.
*/
ws_socket(&evs, 8080, 0, 1000);
return (0);
}
```
the example above can be built with: `make examples`.
## WebSocket client: ToyWS
Inside `extra/toyws` there is a companion project called ToyWS. ToyWS is a very
simple & dumb WebSocket client made exclusively to work with wsServer. Extremely
limited, its usage is highly discouraged with other servers other than wsServer
and is only meant to be used in conjunction with wsServer.
This mini-project only serves as an aid to wsServer and frees the user from
using additional projects to use wsServer in its entirety.
More info at: [extra/toyws/README.md](extra/toyws/README.md)
## SSL/TLS Support
wsServer does not currently support encryption. However, it is possible to use it
in conjunction with [Stunnel](https://www.stunnel.org/), a proxy that adds TLS
support to existing projects. Just follow [these](doc/TLS.md) four easy steps
to get TLS support on wsServer.
## Contributing
wsServer is always open to the community and willing to accept contributions,
whether with issues, documentation, testing, new features, bugfixes, typos...
welcome aboard. Make sure to read the [coding-style](doc/CODING_STYLE.md)
guidelines before sending a PR.
Was the project helpful to you? Have something to say about it? Leave your
comments [here](https://github.com/Theldus/wsServer/discussions/30).
## License and Authors
wsServer is licensed under GPLv3 License. Written by Davidson Francis and
[others](https://github.com/Theldus/wsServer/graphs/contributors)
contributors.

View File

@@ -1,51 +0,0 @@
# Autobahn and standard support
Although wsServer strives to be as simple as possible, the library does intend to used
in production and relies on two (but not limited to) tools for that: [AFL](FUZZING.md)
and Autobahn WebSocket Testsuite. The former ensures that wsServer is stable enough
to be run even under unexpected conditions, such as when under attack. The latter
is discussed below.
[Autobahn|Testsuite](https://github.com/crossbario/autobahn-testsuite) is a third-party
tool that performs a series of automated tests that verify the correctness of client and
server websocket implementations in conformance to the specification. With more than 500
test cases, Autobahn extensively tests client and server implementations and also evaluates
its performance.
## Run tests
Testing requires pre-installation of
[Autobahn|Testsuite](https://github.com/crossbario/autobahn-testsuite). Alternatively, it is
possible to use the Docker image used in the CI tests, in this case, export the environment
variable 'TRAVIS', as `export TRAVIS=true`.
After that, tests can be invoked via Makefile or CMake:
### Makefile
```bash
# Ensure project is in clean state
$ make clean
# Build and execute tests
$ make tests
```
### CMake
```bash
$ mkdir build && cd build/
# Configure project and enable tests build
$ cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_WSSERVER_TEST=On
# Build project
$ make -j4
# Execute tests
$ make test
```
## Tests results
From the [tests](https://theldus.github.io/wsServer/autobahn), it can be seen that
wsServer passes all Autobahn|Testsuite tests. The only tests that are not run (12.*
and 13.*) concern WebSocket Compression, which is defined as an extension of the
websocket protocol and defined in
[RFC 7692](https://datatracker.ietf.org/doc/html/rfc7692). Compression does not
belong to [RFC 6455](https://tools.ietf.org/html/rfc6455).
Therefore, I believe it is safe to say that wsServer is RFC 6455 compliant and should
behave correctly in different scenarios. Any unexpected behavior regarding communication
with the client is considered an error, and an issue must be reported.

View File

@@ -1,62 +0,0 @@
# Building on Windows
## Introduction
Unlike Linux, Windows does not have by default a package manager or native build
tools, which makes the process a little more laborious, but the sections below
clarify in detail what must be done to natively build the library.
## MSYS2 Setup
MSYS2 is a set of tools and libraries that provide an easy-to-use development
environment and is, in my opinion, the most straightforward way to use C in
Windows environments.
The [home page](https://www.msys2.org/) already illustrates the basic
step-by-step for the initial setup of MSYS2 but below is a very brief
description of what to do:
(note: if you already have MinGW and CMake properly installed, you can skip this
section entirely)
### 1) Download and install MSYS2
From the homepage (informed above) download the latest version available, or if
you prefer, use the same version tested here:
[msys2-x86_64-20210604.exe](https://github.com/msys2/msys2-installer/releases/download/2021-06-04/msys2-x86_64-20210604.exe).
### 2) Update packages
MSYS2 uses Pacman, a package manager, that is, a program responsible for
managing programs, libraries, and their dependencies installed on a machine.
Very well known among Arch Linux users, it is very useful to have it on MSYS2.
1) First, it is necessary to update the package database and base packages. If
it hasn't already opened, run 'MSYS2 MSYS' from the Start menu and run
`pacman -Syu`.
2) To proceed with the update, the terminal needs to be closed and opened again.
After that, continue the update with: `pacman -Su`.
### 3) Install packages
In addition to the base packages, some packages need to be installed to have a
minimal development environment:
1) Basic packages and GCC toolchain:
`pacman -S --needed base-devel mingw-w64-x86_64-toolchain`.
2) CMake and Git: `pacman -S mingw-w64-x86_64-cmake git`.
## wsServer build
With MSYS2 up and running, you have everything you need to download, compile and
run the wsServer and its examples.
To do this, run 'MSYS2 MinGW 64bit' from the Start menu and:
```bash
$ git clone https://github.com/Theldus/wsServer.git
$ cd wsServer
$ mkdir build && cd build/
$ cmake .. -DCMAKE_BUILD_TYPE=Release -G "MinGW Makefiles"
$ mingw32-make -j4
```
And that's it, the static library (libws.a) and the examples files are located in the
build/examples folder.
The `echo` example can be run with: `./examples/echo/echo.exe`, and the test webpage can be
found at `wsServer/examples/echo`.

View File

@@ -1,111 +0,0 @@
# wsServer Coding Style
wsServer is a simple project, and therefore I don't believe there is so much
interest in it. However, as I have received a few contributions over the years,
it may be interesting to write something about it.
(much of what is here is (heavily-)based on
[Nanvix](https://github.com/nanvix/documentation/blob/master/6-contrib/3-coding-style.md)'s
and
[Linux](https://www.kernel.org/doc/html/v4.10/process/coding-style.html)'s
coding style.)
## Indentation
- _Always_ use **tab** characters for indentation.
- Tab width: 4 characters.
- Indent case labels in switch statements, such as:
```c
switch (var)
{
case bar:
something;
break;
case beef:
something
break;
default:
break;
}
```
- Do not put multiples statements nor multiples assignments in a single line.
## Long lines length
- Even though Mr. Torvalds has [said](https://www.theregister.com/2020/06/01/linux_5_7/)
that nowadays it is ok to use large lines, I'll keep to 80 columns with five (85)
of tolerance. However, use common sense here.
- While breaking long lines, indent the next line at one level.
## Placing Braces and Spaces
- Allman style: whether functions or control statements, place the braces always
in the next line. Statements within the braces are indented at one level.
Ex:
```c
void foo(void)
{
if (something)
{
bar();
baz();
}
else
deadbeef();
something_else(); /* put always a blank line between a control block
without braces and the next statement. */
}
```
- If one line is enough (like function call or variable assignment), do not put
braces. Keep in mind the readability: put a blank line between statements.
## Spaces
- Use one space after these keywords: `if, switch, case, for, do, while`.
- Use one space around (on each side of) most binary and ternary operators:
`= + - < > * / % | & ^ <= >= == != ? :`.
- But no space after unary operators: `& * + - ~ ! sizeof`.
- No space after/before the postfix/prefix increment & decrement unary operators:
`++ --`.
## Variables and Naming
- Do not use mixed-cases to name your variables. Always use lower-case for
variables and functions name. Underscores are acceptable to give a more descriptive
name, e.g., `int next_frame(...)`.
- Always use uppercase for macros.
- Do not use _typedef_ s unless for totally opaque objects. The reasoning is
simple: it _hides_ the original meaning of the variable. Is it a union, struct,
an integer?.
- (Try to) do not mix variable declaration with your code. Declare your variables
at the very beginning of the scope of that variable.
## Commenting/Documenting
- Use always `/* c89 style. */` in your code, even for a single line.
- Use Doxygen syntax to document functions and structures, even for the internal
API.
---
## Using clang-format
`clang-format` is a command-line tool, part of the LLVM project that, for a
pre-defined set of rules or a given file, automatically formats source code.
wsServer comes with a .clang-format file in the root directory that tries
to embrace most of these rules above.
Please note that the .clang-format file does not fully cover everything described
here and even in cases that do, it is always important to carefully evaluate the
output generated by it, since sometimes it may be far from ideal, use common
sense here.
My suggestion would be something like:
- Commit what you need to commit (**locally**).
- Apply clang-format over the file: `clang-format -style=file -i src/file.c`.
- Manually check changes on the file.
- Make a commit amend if applicable.
Similarly, instead of performing an amend, you can also generate a newly formatted
file and then perform a diff of the original with the new one:
```bash
clang-format -style=file file.c > newfile.c
diff -u file.c newfile.c > diffs.patch # see this file
```
Pick what you think is best.

View File

@@ -1,133 +0,0 @@
# Fuzzing wsServer
wsServer intends to be robust enough to be used safely in production. Thus,
the project supports fuzzing through the `ws_file` routine, which reads a file
containing previously captured packets from the network. This routine allows
wsServer to be tested for common cases and expected to work and permits it to
be used on fuzzers, such as the AFL, supported here.
## 1) Installing/building American Fuzzy Lop
While not the focus here, building AFL should not be an issue, so the
following brief instructions should be sufficient:
```bash
# Clone the repository
git clone https://github.com/google/AFL
# Build
cd AFL/
make
# Set env vars
export PATH=$PATH:$(pwd)
export AFL_PATH=$(pwd)
# Add env vars into your ~/.bashrc
echo "export PATH=\$PATH:$(pwd)/" >> ~/.bashrc
echo "AFL_PATH=$(pwd)" >> ~/.bashrc
```
If anything fails, please check if you have the common build tools on your
system (such as `gcc`, `make`, etc.) and read the official or specific
instructions for your system.
## 2) Fuzzing
Once AFL is up and running, fuzzing is pretty straightforward:
```bash
# Make sure wsServer is in a clean state
make clean
# Build with AFL_FUZZ var set to yes:
AFL_FUZZ=yes make
```
wsServer and the test file will be compiled. Fuzzing starts automatically
right after.
---
## Input tests and file structures
All fuzzing-related parts are present in the _tests_ folder and follow the
following structure:
```text
├── in
│   ├── ch_1b_1b_508b_close
│   └── ch_1b_close
├── out
├── packets
│   ├── ch_508b_close
│   ├── ch_close
│   ├── ff_1b_close
│   ├── ff_384kB_close
│   ├── ff_ping_ping_close
│   ├── ws_1b_close
│   ├── ws_508b_ping_close
│   ├── ws_98305b_close
│ │
│   ├── frames
│   │   ├── close
│   │   ├── ping
│   │   ├── req_chrome
│   │   ├── req_firefox
│   │   └── req_websocat
│   │
│ └── msgs
│      ├── msg_1byte
│      ├── msg_384kB_cont
│      ├── msg_508bytes
│      └── msg_98305bytes
├── Makefile
├── run-fuzzy.sh
└── ws_file.c
```
- **in/:**
Contains the input files that will be used in AFL (parameter `-i`).
- **out/:**
Contains the AFL output (parameter `-o`). Note that the execution script
(`run-fuzzy.sh`) allows you to customize the output by the environment variable
`AFL_OUT`.
- **packets/:**
Contains packets and parts of packets captured from the network from multiple
clients (currently: Firefox, Google Chrome, and Websocat) with wsServer. It
serves as a way to 'assemble' new test files for wsServer, whether for fuzzing
or not.
- **packets/frames:/**
Contains request frames (handshake) and control frames from multiple clients.
- **packets/msgs/:**
Contains packets of messages sent to wsServer of varying sizes, with FRAMES of
type `FIN` and `CONT`.
### Creating new inputs
New input files are pretty simple to make: either you create from existing
packets or capture new ones via tcpdump, wireshark, etc.
Let's say you want to create one that uses a Firefox handshake, sends two messages
of one byte each, one ping and disconnect, we can then do:
```bash
cd tests/packets
cat frames/req_firefox \
msgs/msg_1byte msgs/msg_1byte \
frames/ping \
frames/close > ../in/ff_1b_1b_ping_close
```
For new packets, the idea is similar.
**Attention:** Since inputs need to be valid, when creating new packets, be
sure to always use a handshake (req_*) as the first file and a close frame
at the end.
---
## Acknowledgments
Thanks to [@rlaneth](https://github.com/rlaneth), who performed fuzzing
tests on wsServer and who discovered and helped me to fix many bugs in the
source code.

View File

@@ -1,70 +0,0 @@
## SSL/TLS Support
wsServer does not currently support encryption. However, it is possible to use it in conjunction
with [Stunnel](https://www.stunnel.org/), a proxy that adds TLS support to existing projects.
Just follow these four easy steps below to get TLS support on wsServer.
### 1) Installing Stunnel
#### Ubuntu
```bash
$ sudo apt install stunnel
```
#### Other distros
```bash
$ sudo something
```
### 2) Generating certificates/keys
After you have Stunnel installed, generate your CA, private key and copy
to the Stunnel configure folder (usually /etc/stunnel/, but could be anywhere):
```bash
# Private key
$ openssl genrsa -out server.key 2048
# Certificate Signing Request (CSR)
$ openssl req -new -key server.key -out server.csr
# Certificate
$ openssl x509 -req -days 1024 -in server.csr -signkey server.key -out server.crt
# Append private key, certificate and copy to the right place
$ cat server.key server.crt > server.pem
$ sudo cp server.pem /etc/stunnel/
```
Observations regarding localhost:
1) If you want to run on localhost, the 'Common Name' field (on CSR, 2nd command) _must_
be 'localhost' (without quotes).
2) Make sure to add your .crt file to your browser's Certificate Authorities before trying
to use wsServer with TLS.
3) Google Chrome does not like localhost SSL/TLS traffic, so you need to enable
it first, go to `chrome://flags/#allow-insecure-localhost` and enable this option.
Firefox looks ok as long as you follow 2).
### 3) Stunnel configuration file
Stunnel works by creating a proxy server on a given port that connects to the
original server on another, so we need to teach how it will talk to wsServer:
Create a file /etc/stunnel/stunnel.conf with the following content:
```text
[wsServer]
cert = /etc/stunnel/server.pem
accept = 0.0.0.0:443
connect = 127.0.0.1:8080
```
### 4) Launch Stunnel and wsServer
```bash
$ sudo stunnel /etc/stunnel/stunnel.conf
$ ./your_program_that_uses_wsServer
```
(Many thanks to [@rlaneth](https://github.com/rlaneth) for letting me know of this tool).

View File

@@ -1,5 +0,0 @@
@INCLUDE = doxy.conf
GENERATE_HTML = NO
GENERATE_XML = YES
XML_PROGRAMLISTING = YES
OUTPUT_DIRECTORY = ../../doc/doxygen

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
{% set navbar_current = 'annotated' %}
{% extends 'base-index.html' %}
{% block main %}
<h1>Classes</h1>
<ul class="m-doc">
{% for i in index.symbols recursive %}
{% if i.children %}
<li class="m-doc-collapsible{% if loop.depth > CLASS_INDEX_EXPAND_LEVELS or (i.kind != 'namespace' and not CLASS_INDEX_EXPAND_INNER) %} collapsed{% endif %}">
<a href="#" onclick="return toggle(this)">{{ i.kind }}</a> <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.is_inline %} <span class="m-label m-flat m-info">inline</span>{% endif %}{% if i.is_final %} <span class="m-label m-flat m-warning">final</span>{% endif %}{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span>
<ul class="m-doc">
{{ loop(i.children)|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li>{{ i.kind }} <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.is_inline %} <span class="m-label m-flat m-info">inline</span>{% endif %}{% if i.is_final %} <span class="m-label m-flat m-warning">final</span>{% endif %}{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span></li>
{% endif %}
{% endfor %}
</ul>
{{ super() -}}
{% endblock %}

View File

@@ -1,559 +0,0 @@
{% extends 'base.html' %}
{% macro entry_class(class) %}{% include 'entry-class.html' %}{% endmacro %}
{% macro entry_typedef(typedef, mark_nonpublic=False) %}{% include 'entry-typedef.html' %}{% endmacro %}
{% macro entry_enum(enum, mark_nonpublic=False) %}{% include 'entry-enum.html' %}{% endmacro %}
{% macro entry_func(func, mark_nonpublic=False) %}{% include 'entry-func.html' %}{% endmacro %}
{% macro entry_var(var, mark_nonpublic=False) %}{% include 'entry-var.html' %}{% endmacro %}
{% macro entry_define(define) %}{% include 'entry-define.html' %}{% endmacro %}
{% macro details_typedef(typedef, prefix) %}{% include 'details-typedef.html' %}{% endmacro %}
{% macro details_enum(enum, prefix) %}{% include 'details-enum.html' %}{% endmacro %}
{% macro details_func(func, prefix) %}{% include 'details-func.html' %}{% endmacro %}
{% macro details_var(var, prefix) %}{% include 'details-var.html' %}{% endmacro %}
{% macro details_define(define) %}{% include 'details-define.html' %}{% endmacro %}
{% block title %}{% set j = joiner('::') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} {{ compound.kind }} | {{ super() }}{% endblock %}
{% block main %}
<h1>
{% if compound.templates != None %}
{% if compound.include %}
<div class="m-doc-include m-code m-inverted m-right-m m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ compound.include[1] }}">{{ compound.include[0] }}</a></div>
{% endif %}
{% set j = joiner(', ') %}
<div class="m-doc-template">template&lt;{% for t in compound.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}&gt;</div>
{% endif %}
{%+ for name, target in compound.breadcrumb[:-1] %}<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a>::<wbr/></span>{% endfor %}{{ compound.breadcrumb[-1][0] }} <span class="m-thin">{{ compound.kind }}</span>{% if compound.is_final %} <span class="m-label m-flat m-warning">final</span>{% endif %}{% if compound.since %} {{ compound.since }}{% endif %}
{# need an explicit space here otherwise the newline gets removed #}
{% if compound.include and compound.templates == None %}
<div class="m-doc-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ compound.include[1] }}">{{ compound.include[0] }}</a></div>
{% endif %}
</h1>
{% if compound.brief %}
<p>{{ compound.brief }}</p>
{% endif %}
{% if compound.has_template_details %}
<table class="m-table m-fullwidth m-flat">
<thead>
<tr><th colspan="2">Template parameters</th></tr>
</thead>
<tbody>
{% for template in compound.templates|selectattr('name') %}
<tr>
<td{% if loop.index == 1 %} style="width: 1%"{% endif %}>{{ template.name }}</td>
<td>{{ template.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if compound.sections or compound.public_types or compound.public_static_funcs or compound.typeless_funcs or compound.public_funcs or compound.signals or compound.public_slots or compound.public_static_vars or compound.public_vars or compound.protected_types or compound.protected_static_funcs or compound.protected_funcs or compound.protected_signals or compound.protected_static_vars or compound.protected_vars or compound.private_funcs or compound.private_slots or compound.groups or compound.friend_funcs or compound.related %}
<div class="m-block m-default">
<h3>Contents</h3>
<ul>
{% for id, name, children in compound.sections recursive %}
{% if children %}
<li>
<a href="#{{ id }}">{{ name }}</a>
<ul>
{{ loop(children)|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li><a href="#{{ id }}">{{ name }}</a></li>
{% endif %}
{% endfor %}
<li>
Reference
<ul>
{% if compound.base_classes %}
<li><a href="#base-classes">Base classes</a></li>
{% endif %}
{% if compound.derived_classes %}
<li><a href="#derived-classes">Derived classes</a></li>
{% endif %}
{% if compound.public_types %}
<li><a href="#pub-types">Public types</a></li>
{% endif %}
{% if compound.public_static_vars %}
<li><a href="#pub-static-attribs">Public static variables</a></li>
{% endif %}
{% if compound.public_static_funcs %}
<li><a href="#pub-static-methods">Public static functions</a></li>
{% endif %}
{% if compound.typeless_funcs %}
<li><a href="#typeless-methods">Constructors, destructors, conversion operators</a></li>
{% endif %}
{% if compound.public_funcs %}
<li><a href="#pub-methods">Public functions</a></li>
{% endif %}
{% if compound.signals %}
<li><a href="#signals">Signals</a></li>
{% endif %}
{% if compound.public_slots %}
<li><a href="#pub-slots">Public slots</a></li>
{% endif %}
{% if compound.public_vars %}
<li><a href="#pub-attribs">Public variables</a></li>
{% endif %}
{% if compound.protected_types %}
<li><a href="#pro-types">Protected types</a></li>
{% endif %}
{% if compound.protected_static_funcs %}
<li><a href="#pro-static-methods">Protected static functions</a></li>
{% endif %}
{% if compound.protected_funcs %}
<li><a href="#pro-methods">Protected functions</a></li>
{% endif %}
{% if compound.protected_slots %}
<li><a href="#pro-slots">Protected slots</a></li>
{% endif %}
{% if compound.protected_static_vars %}
<li><a href="#pro-static-attribs">Protected static variables</a></li>
{% endif %}
{% if compound.protected_vars %}
<li><a href="#pro-attribs">Protected variables</a></li>
{% endif %}
{% if compound.private_funcs %}
<li><a href="#pri-methods">Private functions</a></li>
{% endif %}
{% if compound.private_slots %}
<li><a href="#pri-slots">Private slots</a></li>
{% endif %}
{% for group in compound.groups %}
<li><a href="#{{ group.id }}">{{ group.name }}</a></li>
{% endfor %}
{% if compound.friend_funcs %}
<li><a href="#friends">Friends</a></li>
{% endif %}
{% if compound.related %}
<li><a href="#related">Related</a></li>
{% endif %}
</ul>
</li>
</ul>
</div>
{% endif %}
{% if compound.description %}
{{ compound.description }}
{% endif %}
{% if compound.base_classes %}
<section id="base-classes">
<h2><a href="#base-classes">Base classes</a></h2>
<dl class="m-doc">
{% for class in compound.base_classes %}
{{ entry_class(class) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.derived_classes %}
<section id="derived-classes">
<h2><a href="#derived-classes">Derived classes</a></h2>
<dl class="m-doc">
{% for class in compound.derived_classes %}
{{ entry_class(class) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.public_types %}
<section id="pub-types">
<h2><a href="#pub-types">Public types</a></h2>
<dl class="m-doc">
{% for kind, type in compound.public_types %}
{% if kind == 'class' %}
{{ entry_class(type) }}
{% elif kind == 'enum' %}
{{ entry_enum(type) }}
{% elif kind == 'typedef' %}
{{ entry_typedef(type) }}
{% endif %}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.public_static_vars %}
<section id="pub-static-attribs">
<h2><a href="#pub-static-attribs">Public static variables</a></h2>
<dl class="m-doc">
{% for var in compound.public_static_vars %}
{{ entry_var(var) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.public_static_funcs %}
<section id="pub-static-methods">
<h2><a href="#pub-static-methods">Public static functions</a></h2>
<dl class="m-doc">
{% for func in compound.public_static_funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.typeless_funcs %}
<section id="typeless-methods">
<h2><a href="#typeless-methods">Constructors, destructors, conversion operators</a></h2>
<dl class="m-doc">
{% for func in compound.typeless_funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.public_funcs %}
<section id="pub-methods">
<h2><a href="#pub-methods">Public functions</a></h2>
<dl class="m-doc">
{% for func in compound.public_funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.signals %}
<section id="signals">
<h2><a href="#signals">Signals</a></h2>
<dl class="m-doc">
{% for func in compound.signals %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.public_slots %}
<section id="pub-slots">
<h2><a href="#pub-slots">Public slots</a></h2>
<dl class="m-doc">
{% for func in compound.public_slots %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.public_vars %}
<section id="pub-attribs">
<h2><a href="#pub-attribs">Public variables</a></h2>
<dl class="m-doc">
{% for var in compound.public_vars %}
{{ entry_var(var) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.protected_types %}
<section id="pro-types">
<h2><a href="#pro-types">Protected types</a></h2>
<dl class="m-doc">
{% for kind, type in compound.protected_types %}
{% if kind == 'class' %}
{{ entry_class(type) }}
{% elif kind == 'enum' %}
{{ entry_enum(type) }}
{% elif kind == 'typedef' %}
{{ entry_typedef(type) }}
{% endif %}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.protected_static_funcs %}
<section id="pro-static-methods">
<h2><a href="#pro-static-methods">Protected static functions</a></h2>
<dl class="m-doc">
{% for func in compound.protected_static_funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.protected_funcs %}
<section id="pro-methods">
<h2><a href="#pro-methods">Protected functions</a></h2>
<dl class="m-doc">
{% for func in compound.protected_funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.protected_slots %}
<section id="pro-slots">
<h2><a href="#pro-slots">Protected slots</a></h2>
<dl class="m-doc">
{% for func in compound.protected_slots %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.protected_static_vars %}
<section id="pro-static-attribs">
<h2><a href="#pro-static-attribs">Protected static variables</a></h2>
<dl class="m-doc">
{% for var in compound.protected_static_vars %}
{{ entry_var(var) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.protected_vars %}
<section id="pro-attribs">
<h2><a href="#pro-attribs">Protected variables</a></h2>
<dl class="m-doc">
{% for var in compound.protected_vars %}
{{ entry_var(var) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.private_funcs %}
<section id="pri-methods">
<h2><a href="#pri-methods">Private functions</a></h2>
<dl class="m-doc">
{% for func in compound.private_funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.private_slots %}
<section id="pri-slots">
<h2><a href="#pri-slots">Private slots</a></h2>
<dl class="m-doc">
{% for func in compound.private_slots %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% for group in compound.groups %}
<section id="{{ group.id }}">
<h2><a href="#{{ group.id }}">{{ group.name }}</a></h2>
{% if group.description %}
{{ group.description }}
{% endif %}
<dl class="m-doc">
{% for kind, member in group.members %}
{% if kind == 'typedef' %}
{{ entry_typedef(member, mark_nonpublic=True) }}
{% elif kind == 'enum' %}
{{ entry_enum(member, mark_nonpublic=True) }}
{% elif kind == 'func' %}
{{ entry_func(member, mark_nonpublic=True) }}
{% elif kind == 'var' %}
{{ entry_var(member, mark_nonpublic=True) }}
{% endif %}
{% endfor %}
</dl>
</section>
{% endfor %}
{% if compound.friend_funcs %}
<section id="friends">
<h2><a href="#friends">Friends</a></h2>
<dl class="m-doc">
{% for func in compound.friend_funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.related %}
<section id="related">
<h2><a href="#related">Related</a></h2>
<dl class="m-doc">
{% for kind, member in compound.related %}
{% if kind == 'typedef' %}
{{ entry_typedef(member) }}
{% elif kind == 'enum' %}
{{ entry_enum(member) }}
{% elif kind == 'func' %}
{{ entry_func(member) }}
{% elif kind == 'var' %}
{{ entry_var(member) }}
{% elif kind == 'define' %}
{{ entry_define(member) }}
{% endif %}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.has_enum_details %}
<section>
<h2>Enum documentation</h2>
{% for kind, member in compound.public_types %}
{% if kind == 'enum' and member.has_details %}
{{ details_enum(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for kind, member in compound.protected_types %}
{% if kind == 'enum' and member.has_details %}
{{ details_enum(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'enum' and member.has_details %}
{{ details_enum(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% endfor %}
{% for kind, member in compound.related %}
{% if kind == 'enum' and member.has_details %}
{{ details_enum(member, '') }}
{% endif %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_typedef_details %}
<section>
<h2>Typedef documentation</h2>
{% for kind, member in compound.public_types %}
{% if kind == 'typedef' and member.has_details %}
{{ details_typedef(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for kind, member in compound.protected_types %}
{% if kind == 'typedef' and member.has_details %}
{{ details_typedef(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'typedef' and member.has_details %}
{{ details_typedef(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% endfor %}
{% for kind, member in compound.related %}
{% if kind == 'typedef' and member.has_details %}
{{ details_typedef(member, '') }}
{% endif %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_func_details %}
<section>
<h2>Function documentation</h2>
{% for func in compound.public_static_funcs %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.typeless_funcs %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.public_funcs %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.signals %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.public_slots %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.protected_static_funcs %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.protected_funcs %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.protected_slots %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.private_funcs %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for func in compound.private_slots %}
{% if func.has_details %}
{{ details_func(func, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'func' and member.has_details %}
{{ details_func(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% endfor %}
{% for func in compound.friend_funcs %}
{% if func.has_details %}
{{ details_func(func, '') }}
{% endif %}
{% endfor %}
{% for kind, member in compound.related %}
{% if kind == 'func' and member.has_details %}
{{ details_func(member, '') }}
{% endif %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_var_details %}
<section>
<h2>Variable documentation</h2>
{% for var in compound.public_static_vars %}
{% if var.has_details %}
{{ details_var(var, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for var in compound.public_vars %}
{% if var.has_details %}
{{ details_var(var, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for var in compound.protected_static_vars %}
{% if var.has_details %}
{{ details_var(var, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for var in compound.protected_vars %}
{% if var.has_details %}
{{ details_var(var, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'var' and member.has_details %}
{{ details_var(member, compound.prefix_wbr) }}
{% endif %}
{% endfor %}
{% endfor %}
{% for kind, member in compound.related %}
{% if kind == 'var' and member.has_details %}
{{ details_var(member, '') }}
{% endif %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_define_details %}
<section>
<h2>Define documentation</h2>
{% for kind, member in compound.related %}
{% if kind == 'define' and member.has_details %}
{{ details_define(member) }}
{% endif %}
{% endfor %}
</section>
{% endif %}
{% endblock %}

View File

@@ -1,20 +0,0 @@
{% extends 'base.html' %}
{% block main %}
<script>
function toggle(e) {
e.parentElement.className = e.parentElement.className == 'm-doc-collapsible' ?
'm-doc-expansible' : 'm-doc-collapsible';
return false;
}
/* Collapse all nodes marked as such. Doing it via JS instead of
directly in markup so disabling it doesn't harm usability. The list
is somehow regenerated on every iteration and shrinks as I change
the classes. It's not documented anywhere and I'm not sure if this
is the same across browsers, so I am going backwards in that list to
be sure. */
var collapsed = document.getElementsByClassName("collapsed");
for(var i = collapsed.length - 1; i >= 0; --i)
collapsed[i].className = 'm-doc-expansible';
</script>
{% endblock %}

View File

@@ -1,302 +0,0 @@
{% extends 'base.html' %}
{% set prefix = compound.prefix_wbr %}
{% macro entry_module(module) %}{% include 'entry-module.html' %}{% endmacro %}
{% macro entry_dir(dir) %}{% include 'entry-dir.html' %}{% endmacro %}
{% macro entry_file(file) %}{% include 'entry-file.html' %}{% endmacro %}
{% macro entry_namespace(namespace) %}{% include 'entry-namespace.html' %}{% endmacro %}
{% macro entry_class(class) %}{% include 'entry-class.html' %}{% endmacro %}
{% macro entry_enum(enum) %}{% include 'entry-enum.html' %}{% endmacro %}
{% macro entry_typedef(typedef) %}{% include 'entry-typedef.html' %}{% endmacro %}
{% macro entry_func(func) %}{% include 'entry-func.html' %}{% endmacro %}
{% macro entry_var(var) %}{% include 'entry-var.html' %}{% endmacro %}
{% macro entry_define(define) %}{% include 'entry-define.html' %}{% endmacro %}
{% macro details_enum(enum) %}{% include 'details-enum.html' %}{% endmacro %}
{% macro details_typedef(typedef) %}{% include 'details-typedef.html' %}{% endmacro %}
{% macro details_func(func) %}{% include 'details-func.html' %}{% endmacro %}
{% macro details_var(var) %}{% include 'details-var.html' %}{% endmacro %}
{% macro details_define(define) %}{% include 'details-define.html' %}{% endmacro %}
{% block main %}
{% block header %}
{% endblock %}
{% if compound.brief %}
<p>{{ compound.brief }}</p>
{% endif %}
{% if compound.sections or compound.modules or compound.dirs or compound.files or compound.namespaces or compound.classes or compound.typedefs or compound.funcs or compound.vars or compound.defines or compound.groups %}
<div class="m-block m-default">
<h3>Contents</h3>
<ul>
{% for id, name, children in compound.sections recursive %}
{% if children %}
<li>
<a href="#{{ id }}">{{ name }}</a>
<ul>
{{ loop(children)|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li><a href="#{{ id }}">{{ name }}</a></li>
{% endif %}
{% endfor %}
{% if compound.modules or compound.dirs or compound.files or compound.namespaces or compound.classes or compound.typedefs or compound.funcs or compound.vars or compound.defines or compound.groups %}
<li>
Reference
<ul>
{% if compound.modules %}
<li><a href="#groups">Modules</a></li>
{% endif %}
{% if compound.dirs %}
<li><a href="#subdirs">Directories</a></li>
{% endif %}
{% if compound.files %}
<li><a href="#files">Files</a></li>
{% endif %}
{% if compound.namespaces %}
<li><a href="#namespaces">Namespaces</a></li>
{% endif %}
{% if compound.classes %}
<li><a href="#nested-classes">Classes</a></li>
{% endif %}
{% if compound.enums %}
<li><a href="#enum-members">Enums</a></li>
{% endif %}
{% if compound.typedefs %}
<li><a href="#typedef-members">Typedefs</a></li>
{% endif %}
{% if compound.funcs %}
<li><a href="#func-members">Functions</a></li>
{% endif %}
{% if compound.vars %}
<li><a href="#var-members">Variables</a></li>
{% endif %}
{% if compound.defines %}
<li><a href="#define-members">Defines</a></li>
{% endif %}
{% for group in compound.groups %}
<li><a href="#{{ group.id }}">{{ group.name }}</a></li>
{% endfor %}
</ul>
</li>
{% endif %}
</ul>
</div>
{% endif %}
{% if compound.description %}
{{ compound.description }}
{% endif %}
{% if compound.modules %}
<section id="groups">
<h2><a href="#groups">Modules</a></h2>
<dl class="m-doc">
{% for module in compound.modules %}
{{ entry_module(module) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.dirs %}
<section id="subdirs">
<h2><a href="#subdirs">Directories</a></h2>
<dl class="m-doc">
{% for dir in compound.dirs %}
{{ entry_dir(dir) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.files %}
<section id="files">
<h2><a href="#files">Files</a></h2>
<dl class="m-doc">
{% for file in compound.files %}
{{ entry_file(file) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.namespaces %}
<section id="namespaces">
<h2><a href="#namespaces">Namespaces</a></h2>
<dl class="m-doc">
{% for namespace in compound.namespaces %}
{{ entry_namespace(namespace) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.classes %}
<section id="nested-classes">
<h2><a href="#nested-classes">Classes</a></h2>
<dl class="m-doc">
{% for class in compound.classes %}
{{ entry_class(class) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.enums %}
<section id="enum-members">
<h2><a href="#enum-members">Enums</a></h2>
<dl class="m-doc">
{% for enum in compound.enums %}
{{ entry_enum(enum) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.typedefs %}
<section id="typedef-members">
<h2><a href="#typedef-members">Typedefs</a></h2>
<dl class="m-doc">
{% for typedef in compound.typedefs %}
{{ entry_typedef(typedef) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.funcs %}
<section id="func-members">
<h2><a href="#func-members">Functions</a></h2>
<dl class="m-doc">
{% for func in compound.funcs %}
{{ entry_func(func) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.vars %}
<section id="var-members">
<h2><a href="#var-members">Variables</a></h2>
<dl class="m-doc">
{% for var in compound.vars %}
{{ entry_var(var) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% if compound.defines %}
<section id="define-members">
<h2><a href="#define-members">Defines</a></h2>
<dl class="m-doc">
{% for define in compound.defines %}
{{ entry_define(define) }}
{% endfor %}
</dl>
</section>
{% endif %}
{% for group in compound.groups %}
<section id="{{ group.id }}">
<h2><a href="#{{ group.id }}">{{ group.name }}</a></h2>
{% if group.description %}
{{ group.description }}
{% endif %}
<dl class="m-doc">
{% for kind, member in group.members %}
{% if kind == 'namespace' %}
{{ entry_namespace(member) }}
{% elif kind == 'class' %}
{{ entry_class(member) }}
{% elif kind == 'enum' %}
{{ entry_enum(member) }}
{% elif kind == 'typedef' %}
{{ entry_typedef(member) }}
{% elif kind == 'func' %}
{{ entry_func(member) }}
{% elif kind == 'var' %}
{{ entry_var(member) }}
{% elif kind == 'define' %}
{{ entry_define(member) }}
{% endif %}
{% endfor %}
</dl>
</section>
{% endfor %}
{% if compound.has_enum_details %}
<section>
<h2>Enum documentation</h2>
{% for enum in compound.enums %}
{% if enum.has_details %}
{{ details_enum(enum) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'enum' and member.has_details %}
{{ details_enum(member) }}
{% endif %}
{% endfor %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_typedef_details %}
<section>
<h2>Typedef documentation</h2>
{% for typedef in compound.typedefs %}
{% if typedef.has_details %}
{{ details_typedef(typedef) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'typedef' and member.has_details %}
{{ details_typedef(member) }}
{% endif %}
{% endfor %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_func_details %}
<section>
<h2>Function documentation</h2>
{% for func in compound.funcs %}
{% if func.has_details %}
{{ details_func(func) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'func' and member.has_details %}
{{ details_func(member) }}
{% endif %}
{% endfor %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_var_details %}
<section>
<h2>Variable documentation</h2>
{% for var in compound.vars %}
{% if var.has_details %}
{{ details_var(var) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'var' and member.has_details %}
{{ details_var(member) }}
{% endif %}
{% endfor %}
{% endfor %}
</section>
{% endif %}
{% if compound.has_define_details %}
<section>
<h2>Define documentation</h2>
{% for define in compound.defines %}
{% if define.has_details %}
{{ details_define(define) }}
{% endif %}
{% endfor %}
{% for group in compound.groups %}
{% for kind, member in group.members %}
{% if kind == 'define' and member.has_details %}
{{ details_define(member) }}
{% endif %}
{% endfor %}
{% endfor %}
</section>
{% endif %}
{% endblock %}

View File

@@ -1,175 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{% block title %}{{ PROJECT_NAME }}{% if PROJECT_BRIEF %} {{ PROJECT_BRIEF }}{% endif %}{% endblock %}</title>
{% for css in STYLESHEETS %}
<link rel="stylesheet" href="{{ css|basename_or_url|e }}" />
{% endfor %}
{% if FAVICON %}
<link rel="icon" href="{{ FAVICON[0]|basename_or_url|e }}" type="{{ FAVICON[1] }}" />
{% endif %}
{% if not SEARCH_DISABLED and SEARCH_BASE_URL %}
<link rel="search" type="application/opensearchdescription+xml" href="opensearch.xml" title="Search {{ PROJECT_NAME }} documentation" />
{% endif %}
{% block header_links %}
{% endblock %}
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% if THEME_COLOR %}
<meta name="theme-color" content="{{ THEME_COLOR }}" />
{% endif %}
{% if HTML_HEADER %}
{{ HTML_HEADER|rtrim|indent(2) }}
{% endif %}
<style>
li {margin-top: 10px;}
</style>
</head>
<body>
<header><nav id="navigation">
<div class="m-container">
<div class="m-row">
{% if MAIN_PROJECT_URL and PROJECT_BRIEF %}
<span id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">
<a href="{{ MAIN_PROJECT_URL }}">{% if PROJECT_LOGO %}<img src="{{ PROJECT_LOGO|basename_or_url|e }}" alt="" />{% endif %}{{ PROJECT_NAME }}</a> <span class="m-breadcrumb">|</span> <a href="index.html" class="m-thin">{{ PROJECT_BRIEF }}</a>
</span>
{% else %}
<a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">{% if PROJECT_LOGO %}<img src="{{ PROJECT_LOGO|basename_or_url|e }}" alt="" />{% endif %}{{ PROJECT_NAME }}{% if PROJECT_BRIEF %} <span class="m-thin">{{ PROJECT_BRIEF }}</span>{% endif %}</a>
{% endif %}
{% if LINKS_NAVBAR1 or LINKS_NAVBAR2 or not SEARCH_DISABLED %}
<div class="m-col-t-4 m-hide-m m-text-right m-nopadr">
{% if not SEARCH_DISABLED %}
<a href="#search" class="m-doc-search-icon" title="Search" onclick="return showSearch()"><svg style="height: 0.9rem;" viewBox="0 0 16 16">
<path id="m-doc-search-icon-path" d="m6 0c-3.31 0-6 2.69-6 6 0 3.31 2.69 6 6 6 1.49 0 2.85-0.541 3.89-1.44-0.0164 0.338 0.147 0.759 0.5 1.15l3.22 3.79c0.552 0.614 1.45 0.665 2 0.115 0.55-0.55 0.499-1.45-0.115-2l-3.79-3.22c-0.392-0.353-0.812-0.515-1.15-0.5 0.895-1.05 1.44-2.41 1.44-3.89 0-3.31-2.69-6-6-6zm0 1.56a4.44 4.44 0 0 1 4.44 4.44 4.44 4.44 0 0 1-4.44 4.44 4.44 4.44 0 0 1-4.44-4.44 4.44 4.44 0 0 1 4.44-4.44z"/>
</svg></a>
{% endif %}
<a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
<a id="m-navbar-hide" href="#" title="Hide navigation"></a>
</div>
<div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
<div class="m-row">
<ol class="{% if LINKS_NAVBAR2 %}m-col-t-6{% else %}m-col-t-12{% endif %} m-col-m-none">
{% for html, title, link, id, sub in LINKS_NAVBAR1 %}
{% if not sub %}
<li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
{% else %}
<li>
{% if html %}
{{ html }}
{% else %}
<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>
{% endif %}
<ol>
{% for html, title, link, id in sub %}
<li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
{% endfor %}
</ol>
</li>
{% endif %}
{% endfor %}
</ol>
{% if LINKS_NAVBAR2 or not SEARCH_DISABLED %}
{% set start = LINKS_NAVBAR1|length + 1 %}
<ol class="m-col-t-6 m-col-m-none" start="{{ start }}">
{% for html, title, link, id, sub in LINKS_NAVBAR2 %}
{% if not sub %}
<li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
{% else %}
<li>
{% if html %}
{{ html }}
{% else %}
<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>
{% endif %}
<ol>
{% for html, title, link, id in sub %}
<li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
{% endfor %}
</ol>
</li>
{% endif %}
{% endfor %}
{% if not SEARCH_DISABLED %}
<li class="m-show-m"><a href="#search" class="m-doc-search-icon" title="Search" onclick="return showSearch()"><svg style="height: 0.9rem;" viewBox="0 0 16 16">
<use href="#m-doc-search-icon-path" />
</svg></a></li>
{% endif %}
<li class="m-show-m"><a target="_blank" href="https://github.com/Theldus/wsServer" class="m-doc-search-icon" title="GitHub Page">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
</a></li>
</ol>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
</nav></header>
<main><article>
<div class="m-container m-container-inflatable">
<div class="m-row">
<div class="m-col-l-10 m-push-l-1">
{% if PAGE_HEADER %}
{{ PAGE_HEADER|replace('{filename}', FILENAME) }}
{% endif %}
{% block main %}
{% endblock %}
</div>
</div>
</div>
</article></main>
{% if not SEARCH_DISABLED %}
<div class="m-doc-search" id="search">
<a href="#!" onclick="return hideSearch()"></a>
<div class="m-container">
<div class="m-row">
<div class="m-col-m-8 m-push-m-2">
<div class="m-doc-search-header m-text m-small">
<div><span class="m-label m-default">Tab</span> / <span class="m-label m-default">T</span> to search, <span class="m-label m-default">Esc</span> to close</div>
<div id="search-symbolcount">&hellip;</div>
</div>
<div class="m-doc-search-content">
<form{% if SEARCH_BASE_URL %} action="{{ SEARCH_BASE_URL }}#search"{% endif %}>
<input type="search" name="q" id="search-input" placeholder="Loading &hellip;" disabled="disabled" autofocus="autofocus" autocomplete="off" spellcheck="false" />
</form>
<noscript class="m-text m-danger m-text-center">Unlike everything else in the docs, the search functionality <em>requires</em> JavaScript.{% if SEARCH_EXTERNAL_URL %} Enable it or <a href="{{ SEARCH_EXTERNAL_URL|replace('{query}', '') }}">use an external search engine</a>.{% endif %}</noscript>
<div id="search-help" class="m-text m-dim m-text-center">
{{ SEARCH_HELP|rtrim|indent(12) }}
</div>
<div id="search-notfound" class="m-text m-warning m-text-center">Sorry, nothing was found.{% if SEARCH_EXTERNAL_URL %}<br />Maybe try a full-text <a href="#" id="search-external" data-search-engine="{{ SEARCH_EXTERNAL_URL }}">search with external engine</a>?{% endif %}</div>
<ul id="search-results"></ul>
</div>
</div>
</div>
</div>
</div>
<script src="search-v{{ SEARCHDATA_FORMAT_VERSION }}.js"></script>
{% if SEARCH_DOWNLOAD_BINARY %}
<script>
Search.download(window.location.pathname.substr(0, window.location.pathname.lastIndexOf('/') + 1) + 'searchdata-v{{ SEARCHDATA_FORMAT_VERSION }}.bin');
</script>
{% else %}
<script src="searchdata-v{{ SEARCHDATA_FORMAT_VERSION }}.js" async="async"></script>
{% endif %}
{% endif %}
{% if FINE_PRINT %}
<footer><nav>
<div class="m-container">
<div class="m-row">
<div class="m-col-l-10 m-push-l-1">
{% if FINE_PRINT == '[default]' %}
<p>{{ PROJECT_NAME }}{% if PROJECT_BRIEF %} {{ PROJECT_BRIEF }}{% endif %}. Created with {% if DOXYGEN_VERSION %}<a href="https://doxygen.org/">Doxygen</a> {{ DOXYGEN_VERSION }} and {% endif %}<a href="https://mcss.mosra.cz/">m.css</a>.</p>
{% else %}
{{ FINE_PRINT|replace('{doxygen_version}', DOXYGEN_VERSION) }}
{% endif %}
</div>
</div>
</div>
</nav></footer>
{% endif %}
</body>
</html>
{#- sanity checks for variables that should be always defined -#}
{% if FILENAME is not defined %}{{ FILENAME.is_not_defined_the_script_is_broken }}{% endif %}
{% if DOXYGEN_VERSION is not defined %}{{ DOXYGEN_VERSION.is_not_defined_the_script_is_broken }}{% endif %}
{% if SEARCHDATA_FORMAT_VERSION is not defined %}{{ SEARCHDATA_FORMAT_VERSION.is_not_defined_the_script_is_broken }}{% endif %}

View File

@@ -1 +0,0 @@
{% extends 'base-class-reference.html' %}

View File

@@ -1,40 +0,0 @@
<section class="m-doc-details" id="{{ define.id }}"><div>
<h3>
{% set j = joiner(',\n ') %}
<span class="m-doc-wrap-bumper">#define <a href="#{{ define.id }}" class="m-doc-self">{{ define.name }}</a>{% if define.params != None %}(</span><span class="m-doc-wrap">{% for param in define.params %}{{ j() }}{{ param[0] }}{% endfor %}){% endif %}{% if define.since %} {{ define.since }}{% endif %}</span>
{% if define.include %}
<div class="m-doc-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ define.include[1] }}">{{ define.include[0] }}</a></div>
{% endif %}
</h3>
{% if define.brief %}
<p>{{ define.brief }}</p>
{% endif %}
{% if define.has_param_details or define.return_value %}
<table class="m-table m-fullwidth m-flat">
{% if define.has_param_details %}
<thead>
<tr><th colspan="2">Parameters</th></tr>
</thead>
<tbody>
{% for name, description in define.params %}
<tr>
<td{% if loop.index == 1 %} style="width: 1%"{% endif %}>{{ name }}</td>
<td>{{ description }}</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
{% if define.return_value %}
<tfoot>
<tr>
<th{% if not define.has_param_details %} style="width: 1%"{% endif %}>Returns</th>
<td>{{ define.return_value }}</td>
</tr>
</tfoot>
{% endif %}
</table>
{% endif %}
{% if define.description %}
{{ define.description }}
{% endif %}
</div></section>

View File

@@ -1,46 +0,0 @@
<section class="m-doc-details" id="{{ enum.id }}"><div>
<h3>
{% if compound.templates != None %}
<div class="m-doc-template">
{% set j = joiner(', ') %}
template&lt;{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}&gt;
</div>
{% endif %}
enum {% if enum.is_strong %}class {% endif %}{{ prefix }}<a href="#{{ enum.id }}" class="m-doc-self">{{ enum.name }}</a>{% if enum.type %}: {{ enum.type }}{% endif %}{% if enum.is_protected %} <span class="m-label m-warning">protected</span>{% endif %}{% if enum.since %} {{ enum.since }}{% endif %}
{# not sure why there needs to be this space #}
{% if enum.include %}
{# Template info can be only present if the enum is inside a
templated class, but in that case we have global include
information, so no need to handle case where
`enum.include and compound.templates != None` #}
<div class="m-doc-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ enum.include[1] }}">{{ enum.include[0] }}</a></div>
{% endif %}
</h3>
{% if enum.brief %}{# brief can be omitted for anonymous enums #}
<p>{{ enum.brief }}</p>
{% endif %}
{% if enum.description %}
{{ enum.description }}
{% endif %}
{% if enum.has_value_details %}
<table class="m-table m-fullwidth m-flat m-doc">
<thead><tr><th style="width: 1%">Enumerators</th><th></th></tr></thead>
<tbody>
{% for value in enum.values %}
<tr>
<td><a href="#{{ value.id }}" class="m-doc-self" id="{{ value.id }}">{{ value.name }}</a>{% if value.since %} {{ value.since }}{% endif %}</td>
<td>
{% if value.brief %}{# brief is not required for values #}
<p>{{ value.brief }}</p>
{% endif %}
{% if value.description %}{# it can be only brief tho #}
{{ value.description }}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div></section>

View File

@@ -1,95 +0,0 @@
<section class="m-doc-details" id="{{ func.id }}"><div>
<h3>
{% if compound.templates != None or func.templates != None %}
{% if func.include %}
<div class="m-doc-include m-code m-inverted m-right-m m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ func.include[1] }}">{{ func.include[0] }}</a></div>
{% endif %}
<div class="m-doc-template">
{% if compound.templates != None %}
{% set j = joiner(', ') %}
template&lt;{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}&gt;
{% endif %}
{% if func.templates != None %}
{% set j = joiner(', ') %}
template&lt;{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}&gt;
{% endif %}
</div>
{% endif %}
{% set j = joiner(',\n ') %}
<span class="m-doc-wrap-bumper">{{ func.prefix }}{{ func.type }} {{ prefix }}</span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#{{ func.id }}" class="m-doc-self">{{ func.name }}</a>(</span><span class="m-doc-wrap">{% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.is_explicit %} <span class="m-label m-info">explicit</span> {% endif %}{% if func.is_final %} <span class="m-label m-warning">final</span>{% elif func.is_override %} <span class="m-label m-warning">override</span>{% elif func.is_pure_virtual %} <span class="m-label m-warning">pure virtual</span>{% elif func.is_virtual %} <span class="m-label m-warning">virtual</span>{% endif %}{% if func.is_protected %} <span class="m-label m-warning">protected{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_private %} <span class="m-label m-danger">private{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_signal %} <span class="m-label m-success">signal</span>{% elif func.is_slot %} <span class="m-label m-success">public slot</span>{% endif %}{% if func.is_defaulted %} <span class="m-label m-info">defaulted</span>{% endif %}{% if func.is_deleted %} <span class="m-label m-danger">deleted</span>{% endif %}{% if func.is_constexpr %} <span class="m-label m-primary">constexpr</span>{% endif %}{% if func.is_conditional_noexcept %} <span class="m-label m-success">noexcept(…)</span>{% elif func.is_noexcept %} <span class="m-label m-success">noexcept</span>{% endif %}{% if func.since %} {{ func.since }}{% endif %}</span></span>
{% if func.include and compound.templates == None and func.templates == None %}
<div class="m-doc-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ func.include[1] }}">{{ func.include[0] }}</a></div>
{% endif %}
</h3>
{% if func.brief %}
<p>{{ func.brief }}</p>
{% endif %}
{% if func.has_template_details or func.has_param_details or func.return_value or func.return_values or func.exceptions %}
<table class="m-table m-fullwidth m-flat">
{% if func.has_template_details %}
<thead>
<tr><th colspan="2">Template parameters</th></tr>
</thead>
<tbody>
{% for template in func.templates|selectattr('name') %}
<tr>
<td{% if loop.index == 1 %} style="width: 1%"{% endif %}>{{ template.name }}</td>
<td>{{ template.description }}</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
{% if func.has_param_details %}
<thead>
<tr><th colspan="2">Parameters</th></tr>
</thead>
<tbody>
{% for param in func.params|selectattr('name') %}
<tr>
<td{% if loop.index == 1 and not func.has_template_details %} style="width: 1%"{% endif %}>{{ param.name }}{% if param.direction == 'in' %}&nbsp;<span class="m-label m-flat m-info">in</span>{% elif param.direction == 'out' %}&nbsp;<span class="m-label m-flat m-warning">out</span>{% elif param.direction == 'inout' %}&nbsp;<span class="m-label m-flat m-danger">in/out</span>{% endif %}</td>
<td>{{ param.description }}</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
{% if func.return_value %}
{{ '<thead>' if func.return_values or func.exceptions else '<tfoot>' }}
<tr>
<th{% if not func.has_template_details and not func.has_param_details %} style="width: 1%"{% endif %}>Returns</th>
<td>{{ func.return_value }}</td>
</tr>
{{ '</thead>' if func.return_values or func.exceptions else '</tfoot>' }}
{% elif func.return_values %}
<thead>
<tr><th colspan="2">Returns</th></tr>
</thead>
{% endif %}
{% if func.return_values %}
<tbody>
{% for value, description in func.return_values %}
<tr>
<td{% if loop.index == 1 and not func.has_template_details and not func.has_param_details and not func.return_value %} style="width: 1%"{% endif %}>{{ value }}</td>
<td>{{ description }}</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
{% if func.exceptions %}
<thead>
<tr><th colspan="2">Exceptions</th></tr>
</thead>
<tbody>
{% for exception, description in func.exceptions %}
<tr>
<td{% if loop.index == 1 and not func.has_template_details and not func.has_param_details and not func.return_value and not func.return_values %} style="width: 1%"{% endif %}>{{ exception }}</td>
<td>{{ description }}</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
</table>
{% endif %}
{% if func.description %}
{{ func.description }}
{% endif %}
</div></section>

View File

@@ -1,50 +0,0 @@
<section class="m-doc-details" id="{{ typedef.id }}"><div>
<h3>
{% if compound.templates != None or typedef.templates != None %}
{% if typedef.include %}
<div class="m-doc-include m-code m-inverted m-right-m m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ typedef.include[1] }}">{{ typedef.include[0] }}</a></div>
{% endif %}
<div class="m-doc-template">
{% if compound.templates != None %}
{% set j = joiner(', ') %}
template&lt;{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}&gt;
{% endif %}
{% if typedef.templates != None %}
{% set j = joiner(', ') %}
template&lt;{% for t in typedef.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}&gt;
{% endif %}
</div>
{% endif %}
{% if typedef.is_using %}
using {{ prefix }}<a href="#{{ typedef.id }}" class="m-doc-self">{{ typedef.name }}</a> = {{ typedef.type }}{{ typedef.args }}{% if typedef.is_protected %} <span class="m-label m-warning">protected</span>{% endif %}{% if typedef.since %} {{ typedef.since }}{% endif %}
{% else %}
typedef {{ typedef.type }}{% if not typedef.args %} {% endif %}{{ prefix }}<a href="#{{ typedef.id }}" class="m-doc-self">{{ typedef.name }}</a>{{ typedef.args }}{% if typedef.is_protected %} <span class="m-label m-warning">protected</span>{% endif %}{% if typedef.since %} {{ typedef.since }}{% endif %}
{% endif %}
{# the empty line has to be here to prevent the lines from merging #}
{% if typedef.include and compound.templates == None and typedef.templates == None %}
<div class="m-doc-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ typedef.include[1] }}">{{ typedef.include[0] }}</a></div>
{% endif %}
</h3>
{% if typedef.brief %}
<p>{{ typedef.brief }}</p>
{% endif %}
{% if typedef.has_template_details %}
<table class="m-table m-fullwidth m-flat">
<thead>
<tr><th colspan="2">Template parameters</th></tr>
</thead>
<tbody>
{% for template in typedef.templates|selectattr('name') %}
<tr>
<td{% if loop.index == 1 %} style="width: 1%"{% endif %}>{{ template.name }}</td>
<td>{{ template.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if typedef.description %}
{{ typedef.description }}
{% endif %}
</div></section>

View File

@@ -1,46 +0,0 @@
<section class="m-doc-details" id="{{ var.id }}"><div>
<h3>
{% if compound.templates != None or var.templates != None %}
{% if var.include %}
<div class="m-doc-include m-code m-inverted m-right-m m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ var.include[1] }}">{{ var.include[0] }}</a></div>
{% endif %}
<div class="m-doc-template">
{% if compound.templates != None %}
{% set j = joiner(', ') %}
template&lt;{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}&gt;
{% endif %}
{% if var.templates != None %}
{% set j = joiner(', ') %}
template&lt;{% for t in var.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}&gt;
{% endif %}
</div>
{% endif %}
{%+ if var.is_static %}static {% endif %}{{ var.type }} {{ prefix }}<a href="#{{ var.id }}" class="m-doc-self">{{ var.name }}</a>{% if var.is_protected %} <span class="m-label m-warning">protected</span>{% endif %}{% if var.is_constexpr %} <span class="m-label m-primary">constexpr</span>{% endif %}{% if var.since %} {{ var.since }}{% endif %}
{# the empty line needs to be here to prevent the lines from merging #}
{% if var.include and compound.templates == None and var.templates == None %}
<div class="m-doc-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ var.include[1] }}">{{ var.include[0] }}</a></div>
{% endif %}
</h3>
{% if var.brief %}
<p>{{ var.brief }}</p>
{% endif %}
{% if var.has_template_details %}
<table class="m-table m-fullwidth m-flat">
<thead>
<tr><th colspan="2">Template parameters</th></tr>
</thead>
<tbody>
{% for template in var.templates|selectattr('name') %}
<tr>
<td{% if loop.index == 1 %} style="width: 1%"{% endif %}>{{ template.name }}</td>
<td>{{ template.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if var.description %}
{{ var.description }}
{% endif %}
</div></section>

View File

@@ -1,11 +0,0 @@
{% extends 'base-reference.html' %}
{% block title %}{% for name, _ in compound.breadcrumb %}{{ name }}/{% endfor %} directory | {{ super() }}{% endblock %}
{% block header %}
<h1>
{%+ for name, target in compound.breadcrumb[:-1] %}<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a>/</span>{% endfor %}{{ compound.breadcrumb[-1][0] }}<span class="m-breadcrumb">/</span> <span class="m-thin">directory</span>{% if compound.since %} {{ compound.since }}{% endif %}
{# need an explicit space here otherwise the newline gets removed #}
</h1>
{% endblock %}

View File

@@ -1,10 +0,0 @@
<dt>
{% if class.templates != None %}
{% set j = joiner(', ') %}
<div class="m-doc-template">template&lt;{% for t in class.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}&gt;</div>
{% endif %}
{{ class.kind }} <a href="{{ class.url }}" class="m-doc">{{ class.name }}</a>{% if class.is_protected %} <span class="m-label m-flat m-warning">protected</span>{% endif %}{% if class.is_final %} <span class="m-label m-flat m-warning">final</span>{% elif class.is_virtual %} <span class="m-label m-flat m-warning">virtual</span>{% endif %}{% if class.deprecated %} <span class="m-label m-danger">{{ class.deprecated }}</span>{% endif %}{% if class.since %} {{ class.since }}{% endif %}
{# the empty line is above to fix spacing #}
</dt>
<dd>{{ class.brief }}</dd>

View File

@@ -1,5 +0,0 @@
<dt{% if not define.has_details and define.base_url == compound.url %} id="{{ define.id }}"{% endif %}>
{% set j = joiner(',\n ') %}
<span class="m-doc-wrap-bumper">#define <a href="{% if define.base_url != compound.url %}{{ define.base_url }}{% endif %}#{{ define.id }}" class="m-doc{% if not define.has_details and define.base_url == compound.url %}-self{% endif %}">{{ define.name }}</a>{% if define.params != None %}(</span><span class="m-doc-wrap">{% for param in define.params %}{{ j() }}{{ param[0] }}{% endfor %}){% endif %}{% if define.deprecated %} <span class="m-label m-danger">{{ define.deprecated }}</span>{% endif %}{% if define.since %} {{ define.since }}{% endif %}</span>
</dt>
<dd>{{ define.brief }}</dd>

View File

@@ -1,2 +0,0 @@
<dt>directory <a href="{{ dir.url }}" class="m-doc">{{ dir.name }}</a>/{% if dir.deprecated %} <span class="m-label m-danger">{{ dir.deprecated }}</span>{% endif %}{% if dir.since %} {{ dir.since }}{% endif %}</dt>
<dd>{{ dir.brief }}</dd>

View File

@@ -1,5 +0,0 @@
<dt{% if not enum.has_details and enum.base_url == compound.url %} id="{{ enum.id }}"{% endif %}>
{% set j = joiner(',\n ') %}
<span class="m-doc-wrap-bumper">enum {% if enum.is_strong %}class {% endif %}<a href="{% if enum.base_url != compound.url %}{{ enum.base_url }}{% endif %}#{{ enum.id }}" class="m-doc{% if not enum.has_details and enum.base_url == compound.url %}-self{% endif %}">{{ enum.name }}</a>{% if enum.type %}: {{ enum.type }}{% endif %} { </span><span class="m-doc-wrap">{% for value in enum.values %}{{ j() }}<a href="#{{ value.id }}" class="m-doc">{{ value.name }}</a>{% if value.initializer %} {{ value.initializer }}{% endif %}{% if value.since %} {{ value.since }}{% endif %}{% if value.deprecated %} <span class="m-label m-danger">{{ value.deprecated }}</span>{% endif %}{% endfor %} }{% if enum.deprecated %} <span class="m-label m-danger">{{ enum.deprecated }}</span>{% endif %}{% if mark_nonpublic and enum.is_protected %} <span class="m-label m-flat m-warning">protected</span>{% endif %}{% if enum.since %} {{ enum.since }}{% endif %}</span>
</dt>
<dd>{{ enum.brief }}</dd>

View File

@@ -1,2 +0,0 @@
<dt>file <a href="{{ file.url }}" class="m-doc">{{ file.name }}</a>{% if file.deprecated %} <span class="m-label m-danger">{{ file.deprecated }}</span>{% endif %}{% if file.since %} {{ file.since }}{% endif %}</dt>
<dd>{{ file.brief }}</dd>

View File

@@ -1,9 +0,0 @@
<dt{% if not func.has_details and func.base_url == compound.url %} id="{{ func.id }}"{% endif %}>
{% if func.templates != None %}
{% set j = joiner(', ') %}
<div class="m-doc-template">template&lt;{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}&gt;</div>
{% endif %}
{% set j = joiner(',\n ') %}
<span class="m-doc-wrap-bumper">{{ func.prefix }}{% if func.type == 'void' %}void {% elif func.type %}auto {% endif %}<a href="{% if func.base_url != compound.url %}{{ func.base_url }}{% endif %}#{{ func.id }}" class="m-doc{% if not func.has_details and func.base_url == compound.url %}-self{% endif %}">{{ func.name }}</a>(</span><span class="m-doc-wrap">{% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.type and func.type != 'void' %} -&gt; {{ func.type }}{% endif %}{% if func.deprecated %} <span class="m-label m-danger">{{ func.deprecated }}</span>{% endif %}{% if not func.type or mark_nonpublic %}{% if func.is_protected %} <span class="m-label m-flat m-warning">protected{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_private %} <span class="m-label m-flat m-danger">private{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_signal %} <span class="m-label m-flat m-success">signal</span>{% elif func.is_slot %} <span class="m-label m-flat m-success">public slot</span>{% endif %}{% endif %}{% if func.is_defaulted %} <span class="m-label m-flat m-info">defaulted</span>{% endif %}{% if func.is_deleted %} <span class="m-label m-flat m-danger">deleted</span>{% endif %}{% if func.is_explicit %} <span class="m-label m-flat m-info">explicit</span> {% endif %}{% if func.is_final %} <span class="m-label m-flat m-warning">final</span>{% elif func.is_override %} <span class="m-label m-flat m-warning">override</span>{% elif func.is_pure_virtual %} <span class="m-label m-flat m-warning">pure virtual</span>{% elif func.is_virtual %} <span class="m-label m-flat m-warning">virtual</span>{% endif %}{% if func.is_constexpr %} <span class="m-label m-flat m-primary">constexpr</span>{% endif %}{% if func.is_conditional_noexcept %} <span class="m-label m-flat m-success">noexcept(…)</span>{% elif func.is_noexcept %} <span class="m-label m-flat m-success">noexcept</span>{% endif %}{% if func.since %} {{ func.since }}{% endif %}</span>
</dt>
<dd>{{ func.brief }}</dd>

View File

@@ -1,2 +0,0 @@
<dt>module <a href="{{ module.url }}" class="m-doc">{{ module.name }}</a>{% if module.deprecated %} <span class="m-label m-danger">{{ module.deprecated }}</span>{% endif %}{% if module.since %} {{ module.since }}{% endif %}</dt>
<dd>{{ module.brief }}</dd>

View File

@@ -1,2 +0,0 @@
<dt>namespace <a href="{{ namespace.url }}" class="m-doc">{{ namespace.name }}</a>{% if namespace.is_inline %} <span class="m-label m-flat m-info">inline</span>{% endif %}{% if namespace.deprecated %} <span class="m-label m-danger">{{ namespace.deprecated }}</span>{% endif %}{% if namespace.since %} {{ namespace.since }}{% endif %}</dt>
<dd>{{ namespace.brief }}</dd>

View File

@@ -1,10 +0,0 @@
<dt{% if not typedef.has_details and typedef.base_url == compound.url %} id="{{ typedef.id }}"{% endif %}>
{% if typedef.templates != None %}
{% set j = joiner(', ') %}
<div class="m-doc-template">template&lt;{% for t in typedef.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}&gt;</div>
{% endif %}
using <a href="{% if typedef.base_url != compound.url %}{{ typedef.base_url }}{% endif %}#{{ typedef.id }}" class="m-doc{% if not typedef.has_details and typedef.base_url == compound.url %}-self{% endif %}">{{ typedef.name }}</a> = {{ typedef.type }}{{ typedef.args }}{% if typedef.deprecated %} <span class="m-label m-danger">{{ typedef.deprecated }}</span>{% endif %}{% if mark_nonpublic and typedef.is_protected %} <span class="m-label m-flat m-warning">protected</span>{% endif %}{% if typedef.since %} {{ typedef.since }}{% endif %}
{# This empty line needs to be there otherwise it's eaten #}
</dt>
<dd>{{ typedef.brief }}</dd>

View File

@@ -1,10 +0,0 @@
<dt{% if not var.has_details and var.base_url == compound.url %} id="{{ var.id }}"{% endif %}>
{% if var.templates != None %}
{% set j = joiner(', ') %}
<div class="m-doc-template">template&lt;{% for t in var.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}&gt;</div>
{% endif %}
{%+ if var.is_static %}static {% endif %}{{ var.type }} <a href="{% if var.base_url != compound.url %}{{ var.base_url }}{% endif %}#{{ var.id }}" class="m-doc{% if not var.has_details and var.base_url == compound.url %}-self{% endif %}">{{ var.name }}</a>{% if var.deprecated %} <span class="m-label m-danger">{{ var.deprecated }}</span>{% endif %}{% if mark_nonpublic and var.is_protected %} <span class="m-label m-flat m-warning">protected</span>{% endif %}{% if var.is_constexpr %} <span class="m-label m-flat m-primary">constexpr</span>{% endif %}{% if var.since %} {{ var.since }}{% endif %}
{# This empty line needs to be there otherwise it's eaten #}
</dt>
<dd>{{ var.brief }}</dd>

View File

@@ -1,30 +0,0 @@
{% extends 'base.html' %}
{% block title %}{% set j = joiner(' &raquo; ') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} source | {{ super() }}{% endblock %}
{% block header_links %}
{% if compound.footer_navigation and compound.footer_navigation[0] %}
<link rel="prev" href="{{ compound.footer_navigation[0][0] }}" />
{% endif %}
{% if compound.footer_navigation and compound.footer_navigation[2] %}
<link rel="next" href="{{ compound.footer_navigation[2][0] }}" />
{% endif %}
{% endblock %}
{% block main %}
<h1>
{% for name, target in compound.breadcrumb[:-1] %}
<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a> &raquo;</span>
{% endfor %}
{{ compound.breadcrumb[-1][0] }} <span class="m-thin">source</span>
</h1>
{% if compound.brief %}
<p>{{ compound.brief }}</p>
{% endif %}
{% if compound.description %}
{{ compound.description }}
{% endif %}
{% if compound.footer_navigation %}
<div class="m-note m-dim m-thin m-text-center">{% if compound.footer_navigation[0] %}<a href="{{ compound.footer_navigation[0][0] }}" class="m-doc">&laquo; {{ compound.footer_navigation[0][1] }}</a> | {% endif %}<a href="{{ compound.footer_navigation[1][0] }}" class="m-doc">{{ compound.footer_navigation[1][1] }}</a>{% if compound.footer_navigation[2] %} | <a href="{{ compound.footer_navigation[2][0] }}" class="m-doc">{{ compound.footer_navigation[2][1] }} &raquo;</a>{% endif %}</div>
{% endif %}
{% endblock %}

View File

@@ -1,11 +0,0 @@
{% extends 'base-reference.html' %}
{% block title %}{% set j = joiner('/') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} file | {{ super() }}{% endblock %}
{% block header %}
<h1>
{%+ for name, target in compound.breadcrumb[:-1] %}<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a>/</span>{% endfor %}{{ compound.breadcrumb[-1][0] }} <span class="m-thin">file</span>{% if compound.since %} {{ compound.since }}{% endif %}
{# need an explicit space here otherwise the newline gets removed #}
</h1>
{% endblock %}

View File

@@ -1,21 +0,0 @@
{% set navbar_current = 'files' %}
{% extends 'base-index.html' %}
{% block main %}
<h1>Files</h1>
<ul class="m-doc">
{% for i in index.files recursive %}
{% if i.children %}
<li class="m-doc-collapsible{% if loop.depth > FILE_INDEX_EXPAND_LEVELS %} collapsed{% endif %}">
<a href="#" onclick="return toggle(this)">{{ i.kind }}</a> <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span>
<ul class="m-doc">
{{ loop(i.children)|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li>{{ i.kind }} <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span></li>
{% endif %}
{% endfor %}
</ul>
{{ super() -}}
{% endblock %}

View File

@@ -1,11 +0,0 @@
{% extends 'base-reference.html' %}
{% block title %}{% set j = joiner(' &raquo; ') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} module | {{ super() }}{% endblock %}
{% block header %}
<h1>
{% for name, target in compound.breadcrumb[:-1] %}
<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a> &raquo;</span>
{% endfor %}
{{ compound.breadcrumb[-1][0] }} <span class="m-thin">module</span>{% if compound.since %} {{ compound.since }}{% endif %}</h1>
{% endblock %}

View File

@@ -1,21 +0,0 @@
{% set navbar_current = 'modules' %}
{% extends 'base-index.html' %}
{% block main %}
<h1>Modules</h1>
<ul class="m-doc">
{% for i in index.modules recursive %}
{% if i.has_nestable_children %}
<li class="m-doc-collapsible">
<a href="#" onclick="return toggle(this)">module</a> <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span>
<ul class="m-doc">
{{ loop(i.children)|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li>module <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span></li>
{% endif %}
{% endfor %}
</ul>
{{ super() -}}
{% endblock %}

View File

@@ -1,14 +0,0 @@
{% extends 'base-reference.html' %}
{% block title %}{% set j = joiner('::') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} namespace | {{ super() }}{% endblock %}
{% block header %}
<h1>
{%+ for name, target in compound.breadcrumb[:-1] %}<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a>::<wbr/></span>{% endfor %}{{ compound.breadcrumb[-1][0] }} <span class="m-thin">namespace</span>{% if compound.is_inline %} <span class="m-label m-flat m-info">inline</span>{% endif %}{% if compound.since %} {{ compound.since }}{% endif %}
{# need an explicit space here otherwise the newline gets removed #}
{% if compound.include %}
<div class="m-doc-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ compound.include[1] }}">{{ compound.include[0] }}</a></div>
{% endif %}
</h1>
{% endblock %}

View File

@@ -1,21 +0,0 @@
{% set navbar_current = 'namespaces' %}
{% extends 'base-index.html' %}
{% block main %}
<h1>Namespaces</h1>
<ul class="m-doc">
{% for i in index.symbols|selectattr('kind', 'equalto', 'namespace') recursive %}
{% if i.has_nestable_children %}
<li class="m-doc-collapsible">
<a href="#" onclick="return toggle(this)">{{ i.kind }}</a> <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.is_inline %} <span class="m-label m-flat m-info">inline</span>{% endif %}{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span>
<ul class="m-doc">
{{ loop(i.children|selectattr('kind', 'equalto', 'namespace'))|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li>{{ i.kind }} <a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.is_inline %} <span class="m-label m-flat m-info">inline</span>{% endif %}{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span></li>
{% endif %}
{% endfor %}
</ul>
{{ super() -}}
{% endblock %}

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>{{ PROJECT_NAME }}{% if PROJECT_BRIEF %} {{ PROJECT_BRIEF }}{% endif %}</ShortName>
<Description>Search {{ PROJECT_NAME }} documentation</Description>
{% if FAVICON %}
<Image type="{{ FAVICON[1] }}">{{ SEARCH_BASE_URL|urljoin(FAVICON[0])|e }}</Image>
{% endif %}
<Url type="text/html" template="{{ SEARCH_BASE_URL }}?q={searchTerms}#search"/>
</OpenSearchDescription>

View File

@@ -1,51 +0,0 @@
{% extends 'base.html' %}
{% block title %}{% if 1 in compound.breadcrumb or compound.breadcrumb[-1][0] != PROJECT_NAME %}{% set j = joiner(' &raquo; ') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} | {{ super() }}{% else %}{{ super() }}{% endif %}{% endblock %}
{% block header_links %}
{% if compound.footer_navigation and compound.footer_navigation[0] %}
<link rel="prev" href="{{ compound.footer_navigation[0][0] }}" />
{% endif %}
{% if compound.footer_navigation and compound.footer_navigation[2] %}
<link rel="next" href="{{ compound.footer_navigation[2][0] }}" />
{% endif %}
{% endblock %}
{% block main %}
<h1>
{% for name, target in compound.breadcrumb[:-1] %}
<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a> &raquo;</span>
{% endfor %}
{{ compound.breadcrumb[-1][0] }}{% if compound.since %} {{ compound.since }}{% endif %}
{# need an explicit space here otherwise the newline gets removed #}
</h1>
{% if compound.brief %}
<p>{{ compound.brief }}</p>
{% endif %}
{% if compound.sections %}
<div class="m-block m-default">
<h3>Contents</h3>
<ul>
{% for id, name, children in compound.sections recursive %}
{% if children %}
<li>
<a href="#{{ id }}">{{ name }}</a>
<ul>
{{ loop(children)|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li><a href="#{{ id }}">{{ name }}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
{% if compound.description %}
{{ compound.description }}
{% endif %}
{% if compound.footer_navigation %}
<div class="m-note m-dim m-thin m-text-center">{% if compound.footer_navigation[0] %}<a href="{{ compound.footer_navigation[0][0] }}" class="m-doc">&laquo; {{ compound.footer_navigation[0][1] }}</a> | {% endif %}<a href="{{ compound.footer_navigation[1][0] }}" class="m-doc">{{ compound.footer_navigation[1][1] }}</a>{% if compound.footer_navigation[2] %} | <a href="{{ compound.footer_navigation[2][0] }}" class="m-doc">{{ compound.footer_navigation[2][1] }} &raquo;</a>{% endif %}</div>
{% endif %}
{% endblock %}

View File

@@ -1,21 +0,0 @@
{% set navbar_current = 'pages' %}
{% extends 'base-index.html' %}
{% block main %}
<h1>Pages</h1>
<ul class="m-doc">
{% for i in index.pages recursive %}
{% if i.children %}
<li class="m-doc-collapsible">
<a href="#" onclick="return toggle(this)"></a><a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span>
<ul class="m-doc">
{{ loop(i.children)|rtrim|indent(4, true) }}
</ul>
</li>
{% else %}
<li><a href="{{ i.url }}" class="m-doc">{{ i.name }}</a>{% if i.deprecated %} <span class="m-label m-danger">{{ i.deprecated }}</span>{% endif %}{% if i.since %} {{ i.since }}{% endif %} <span class="m-doc">{{ i.brief }}</span></li>
{% endif %}
{% endfor %}
</ul>
{{ super() -}}
{% endblock %}

View File

@@ -1 +0,0 @@
{% extends 'base-class-reference.html' %}

View File

@@ -1 +0,0 @@
{% extends 'base-class-reference.html' %}

View File

@@ -1,38 +0,0 @@
.\"
.\" Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "04 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_close_connection \- Close the client connection.
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "int ws_close_client(ws_cli_conn_t " *client ");
.fi
.SH DESCRIPTION
.BR ws_close_client ()
for a given client
.I client
, closes the client connection with normal close code (1000) and no
reason string.
.SH RETURN VALUE
Returns 0 if success, -1 otherwise.
.SH NOTES
If the client did not send a close frame in TIMEOUT_MS ms (500 ms), the
server will close the connection with error code (1002).
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,46 +0,0 @@
.\"
.\" Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "04 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_get_state \- Get a client current state
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "int ws_get_state(ws_cli_conn_t " *client ");
.fi
.SH DESCRIPTION
.BR ws_get_state ()
for a given client
.I client
, gets the current state. Valid states are:
.PP
.RS 2
.IP \(em 2
WS_STATE_CONNECTING (0)
.IP \(em 2
WS_STATE_OPEN (1)
.IP \(em 2
WS_STATE_CLOSING (2)
.IP \(em 2
WS_STATE_CLOSED (3)
.PP
Anything other than that should be considered an error.
.SH RETURN VALUE
Returns the client state or -1 if error.
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,37 +0,0 @@
.\"
.\" Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "04 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_getaddress \- Gets the client IP address
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "char* ws_getaddress(ws_cli_conn_t " "*client" );
.fi
.SH DESCRIPTION
.BR ws_getaddress ()
obtains the client IP address as a string for a given
.I client
passed as parameter.
.SH NOTES
.PP
The returned string is static, no need to free up memory.
.SH RETURN VALUE
Returns a string containing the client IP address or null if error.
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,44 +0,0 @@
.\"
.\" Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "29 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_ping \- Sends a ping to a single client or broadcast
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "void ws_ping(ws_cli_conn_t " *client ", int " broadcast ");"
.fi
.SH DESCRIPTION
.BR ws_ping ()
sends a ping message to the
.I client
(or broadcast if NULL) with a given
.I threshold.
Although wsServer supports sending PINGs, they are not automatic: the user
needs to invoke
.BR ws_ping()
periodically, whether in a separate thread (recommended) or not.
The interval between each call determines the 'timeout' between PINGs.
Threshold must be positive and greater than zero, and determines how many
PINGs can be ignored.
.SH RETURN VALUE
The function does not return any value.
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,66 +0,0 @@
.\"
.\" Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "04 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_sendframe \- Creates and send a masked WebSocket frame with some payload data.
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "int ws_sendframe(ws_cli_conn_t " *client ", const char " *msg ", ssize_t " size ", int " type ");
.fi
.SH DESCRIPTION
.BR ws_sendframe ()
sends a given message
.I msg
to the client
.I client
with the size
.I size
for a given frame
.I type.
This routine is intended to be used to create a websocket frame for
a given type and sending to the client. For higher level routines,
please check
.I ws_sendframe_txt
and
.I ws_sendframe_bin.
.SH RETURN VALUE
Returns the number of bytes written.
.SH NOTES
.PP
The parameter
.I size
can be any value or -1, if the later, the size will be automatically calculated
from the message. Please note that null terminated strings are expected.
.PP
The acceptable values for
.I type
are:
.BR WS_FR_OP_TXT,
.BR WS_FR_OP_BIN
and
.BR WS_FR_OP_PONG.
.PP
If the parameter
.I client
is NULL, a broadcast message will be sent instead.
.SH SEE ALSO
.BR ws_sendframe_txt (3),
.BR ws_sendframe_bin (3)
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,45 +0,0 @@
.\"
.\" Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "04 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_sendframe_bin \- Sends a WebSocket binary frame.
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "int ws_sendframe_bin(ws_cli_conn_t " *client ", const char " *msg ", size " size ");
.fi
.SH DESCRIPTION
.BR ws_sendframe_bin ()
sends a given text message
.I msg
to the client
.I client
with size
.I size
.SH NOTES
.PP
If the parameter
.I client
is NULL, a broadcast message will be sent instead.
.SH RETURN VALUE
Returns the number of bytes written.
.SH SEE ALSO
.BR ws_sendframe (3),
.BR ws_sendframe_txt (3)
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,43 +0,0 @@
.\"
.\" Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "04 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_sendframe_txt \- Sends a WebSocket text frame.
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "int ws_sendframe_txt(ws_cli_conn_t " *client ", const char " *msg ");
.fi
.SH DESCRIPTION
.BR ws_sendframe_txt ()
sends a given text message
.I msg
to the client
.I client
.SH NOTES
.PP
If the parameter
.I client
is NULL, a broadcast message will be sent instead.
.SH RETURN VALUE
Returns the number of bytes written.
.SH SEE ALSO
.BR ws_sendframe (3),
.BR ws_sendframe_bin (3)
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,90 +0,0 @@
.\"
.\" Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation, either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>
.\"
.TH man 3 "04 Apr 2022" "1.0" "wsServer man page"
.SH NAME
ws_socket \- Start the WebSocket server
.SH SYNOPSIS
.nf
.B #include <ws.h>
.sp
.BI "int ws_socket(struct ws_events " *evs ", uint16_t " port ", int " tloop ", "
.BI uint32_t " timeout_ms " );
.fi
.SH DESCRIPTION
.BR ws_socket ()
starts the websocket server for the configured events
.IR evs ,
port number
.IR port .
and
.IR tloop .
The parameter
.I tloop
specifies if the accept loop should run on the same thread (if equals 0)
or on a different thread (if != 0).
Optionally, a user can set a pre-defined
.I timeout_ms
time (in milliseconds) that each message should have. Think of it like a 'ping':
if the client doesn't respond in x amount of time, the client is unresponsive
and the server shouldn't bother sending message to it.
In case of doubt, leave as 0.
.SH RETURN VALUE
If
.I tloop
is equals 0, this function blocks and never returns. Otherwise, returns 0.
.SH NOTES
.PP
The structure
.I evs
is defined as follows:
.nf
struct ws_events
{
void (*onopen)(ws_cli_conn_t *client);
void (*onclose)(ws_cli_conn_t *client);
void (*onmessage)(ws_cli_conn_t *client,
const unsigned char * msg,
uint64_t size, int type);
};
.fi
Each element corresponds to function pointers that are triggered when the
events occur.
The events:
.RS 2
.IP \(em 2
on_open: occurs when a client successfully connects and handshakes with the
server.
.IP \(em 2
on_close: occurs when a valid client (that handshaked with the server)
disconnects with the server. The reason is not informed.
.IP \(em 2
on_message: occurs when a client sends a message (whether txt or bin) to the
server.
.PP
Also note that the thread that sends the events is the same as that deals
with the client connection, so keep in mind that you need to let the
function return. If you want to perform further processing, consider
creating a new thread.
.SH SEE ALSO
.BR ws_sendframe_txt (3),
.BR ws_sendframe_bin (3)
.SH AUTHOR
Davidson Francis (davidsondfgl@gmail.com)

View File

@@ -1,18 +0,0 @@
# Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
add_subdirectory(echo)
add_subdirectory(ping)
add_subdirectory(vtouchpad)

View File

@@ -1,32 +0,0 @@
# Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
.PHONY: all clean
# Examples
all: echo/echo ping/ping
# Echo
echo/echo:
$(MAKE) -C echo/ all
# Ping
ping/ping:
$(MAKE) -C ping/ all
# Clean
clean:
$(MAKE) -C echo/ clean
$(MAKE) -C ping/ clean

View File

@@ -1,20 +0,0 @@
# wsServer examples
This directory contains usage examples and demos for many different scenarios
that might happen when using wsServer.
**The examples:**
- [echo](echo): A simple echo server that broadcasts incoming messages
to all connected clients.
- [vtouchpad](vtouchpad): A 'virtual touchpad' that remotely controls
a computer's mouse.
If you have other examples and/or small demos that might be useful for
illustrating wsServer's functionality, feel free to submit a PR.
## Building
A typical build with `make` or `cmake` should be able to compile all examples
by default. However, note that some examples/demos may be platform specific
(either Linux, Windows...), so check the specific README for each example in
their respective folders for more information.

View File

@@ -1,17 +0,0 @@
# Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
add_executable(echo echo.c)
target_link_libraries(echo ws)

View File

@@ -1,39 +0,0 @@
# Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
CC ?= gcc
WSDIR = $(CURDIR)/../../
INCLUDE = -I $(WSDIR)/include
CFLAGS = -Wall -Wextra -O2
CFLAGS += $(INCLUDE) -std=c99 -pthread -pedantic
LIB = $(WSDIR)/libws.a
# Check if verbose examples
ifeq ($(VERBOSE_EXAMPLES), no)
CFLAGS += -DDISABLE_VERBOSE
endif
.PHONY: all clean
# Examples
all: echo
# Echo
echo: echo.c $(LIB)
$(CC) $(CFLAGS) $(LDFLAGS) echo.c -o echo $(LIB)
# Clean
clean:
@rm -f echo

View File

@@ -1,125 +0,0 @@
/*
* Copyright (C) 2016-2021 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>
/**
* @dir examples/
* @brief wsServer examples folder
*/
/*
* @dir examples/echo
* @brief Echo example directory.
* @file echo.c
* @brief Simple echo example.
*/
/**
* @brief Called when a client connects to the server.
*
* @param client Client connection. The @p client parameter is used
* in order to send messages and retrieve informations about the
* client.
*/
void onopen(ws_cli_conn_t *client)
{
char *cli;
cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
printf("Connection opened, addr: %s\n", cli);
#endif
}
/**
* @brief Called when a client disconnects to the server.
*
* @param client Client connection. The @p client parameter is used
* in order to send messages and retrieve informations about the
* client.
*/
void onclose(ws_cli_conn_t *client)
{
char *cli;
cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
printf("Connection closed, addr: %s\n", cli);
#endif
}
/**
* @brief Called when a client connects to the server.
*
* @param client Client connection. The @p client parameter is used
* in order to send messages and retrieve informations about the
* client.
*
* @param msg Received message, this message can be a text
* or binary message.
*
* @param size Message size (in bytes).
*
* @param type Message type.
*/
void onmessage(ws_cli_conn_t *client,
const unsigned char *msg, uint64_t size, int type)
{
char *cli;
cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
printf("I receive a message: %s (size: %" PRId64 ", type: %d), from: %s\n",
msg, size, type, cli);
#endif
/**
* Mimicks the same frame type received and re-send it again
*
* Please note that we could just use a ws_sendframe_txt()
* or ws_sendframe_bin() here, but we're just being safe
* and re-sending the very same frame type and content
* again.
*
* Client equals to NULL: broadcast
*/
ws_sendframe(NULL, (char *)msg, size, type);
}
/**
* @brief Main routine.
*
* @note After invoking @ref ws_socket, this routine never returns,
* unless if invoked from a different thread.
*/
int main(void)
{
struct ws_events evs;
evs.onopen = &onopen;
evs.onclose = &onclose;
evs.onmessage = &onmessage;
ws_socket(&evs, 8080, 0, 1000); /* Never returns. */
/*
* If you want to execute code past ws_socket, invoke it like:
* ws_socket(&evs, 8080, 1, 1000)
*/
return (0);
}

View File

@@ -1,137 +0,0 @@
<!--
Copyright (C) 2016-2020 Davidson Francis <davidsondfgl@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
-->
<html>
<head>
<script>
/* WebSocket. */
var ws;
var connected = false;
function scoll_to_bottom()
{
var logTa = document.getElementById("taLog")
logTa.scrollTop = logTa.scrollHeight;
}
/* Establish connection. */
function doConnect(addr)
{
/* Message to be sent. */
var msg;
/* Do connection. */
ws = new WebSocket(addr);
/* Register events. */
ws.onopen = function()
{
connected = true;
document.getElementById("btConn").value = "Disconnect!";
document.getElementById("taLog").value += ("Connection opened\n");
scoll_to_bottom();
};
/* Deals with messages. */
ws.onmessage = function (evt)
{
document.getElementById("taLog").value += ("Recv: " + evt.data + "\n");
scoll_to_bottom();
};
/* Close events. */
ws.onclose = function(event)
{
document.getElementById("btConn").value = "Connect!";
document.getElementById("taLog").value +=
("Connection closed: wasClean: " + event.wasClean + ", evCode: "
+ event.code + "\n");
scoll_to_bottom();
connected = false;
};
}
document.addEventListener("DOMContentLoaded", function(event)
{
/* Connect buttom. */
document.getElementById("btConn").onclick = function()
{
if (connected == false)
{
var txt = document.getElementById("txtServer").value;
doConnect(txt);
}
else
{
ws.close();
connected = false;
document.getElementById("btConn").value = "Connect!";
}
};
/* Input text message. */
document.getElementById("txtMsg").addEventListener('keyup', function(e)
{
var key = e.which || e.keyCode;
if (key == 13)
document.getElementById("btMsg").click();
});
/* Send message. */
document.getElementById("btMsg").onclick = function()
{
if (connected == true)
{
var txt = document.getElementById("txtMsg");
var log = document.getElementById("taLog");
ws.send(txt.value);
log.value += ("Send: " + txt.value + "\n");
txt.value = "";
scoll_to_bottom();
}
};
});
</script>
</head>
<body>
<div id="header">
<h1 align="left">wsServer</h1>
Server: <input type="text" id="txtServer" value="ws://localhost:8080">
<input type="button" id="btConn" name="btConn" value="Connect!"><br /><br />
Connection type:
<input type="radio" name="port" id="port1" checked="true" value="ws:// (8080)"
onclick="document.getElementById('txtServer').value='ws://localhost:8080'">
<label for="port1">ws:// (8080)</label>
<input type="radio" name="port" id="port2" value="wss:// (443)"
onclick="document.getElementById('txtServer').value='wss://localhost:443'">
<label for="port2">wss:// (443)</label>
<br /> <br />
Message: <input type="text" id="txtMsg" value=""
placeholder="Type your message (ENTER to send)">
<input type="button" id="btMsg" name="btMsg" value="Send"><br /><br />
<textarea rows="10" cols="50" id="taLog" name="taLog"></textarea>
</div>
</body>
</html>

View File

@@ -1,17 +0,0 @@
# Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
add_executable(ping ping.c)
target_link_libraries(ping ws)

View File

@@ -1,39 +0,0 @@
# Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
CC ?= gcc
WSDIR = $(CURDIR)/../../
INCLUDE = -I $(WSDIR)/include
CFLAGS = -Wall -Wextra -O2
CFLAGS += $(INCLUDE) -std=c99 -pthread -pedantic
LDLIBS += $(WSDIR)/libws.a
TARGET = ping
.PHONY: all clean
# Build objects rule
%.o: %.c
$(CC) $< $(CFLAGS) $(INCLUDE) -c -o $@
all: $(TARGET)
# Ping
$(TARGET): $(TARGET).o $(LDLIBS)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS)
# Clean
clean:
@rm -f $(TARGET)
@rm -f *.o

View File

@@ -1,104 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>
/**
* @dir examples/ping
* @brief Ping example directory
*
* @file ping.c
* @brief Main file.
*/
/**
* @brief Called when a client connects to the server.
*
* @param client Client connection.
*/
void onopen(ws_cli_conn_t *client)
{
((void)client);
printf("Connected!\n");
}
/**
* @brief Called when a client disconnects to the server.
*
* @param client Client connection.
*/
void onclose(ws_cli_conn_t *client)
{
((void)client);
printf("Disconnected!\n");
}
/**
* @brief Called when a client connects to the server.
*
* @param client Client connection.
*
* @param msg Received message.
* @param size Message size (in bytes).
* @param type Message type.
*/
void onmessage(ws_cli_conn_t *client,
const unsigned char *msg, uint64_t size, int type)
{
((void)client);
((void)msg);
((void)size);
((void)type);
}
/**
* @brief Main routine.
*/
int main(void)
{
struct ws_events evs;
evs.onopen = &onopen;
evs.onclose = &onclose;
evs.onmessage = &onmessage;
ws_socket(&evs, 8080, 1, 1000);
/*
* Periodically send ping frames in the main thread
* and aborts inactive connections.
*/
while (1)
{
/*
* Sends a broadcast PING with 2-DELAY MS of tolerance, i.e:
* the client can miss up to 2 PINGs messages.
*
* The 'timeout' is specified by the time between ws_ping()
* calls. In this example, 10 seconds.
*/
printf("Sending ping...\n");
ws_ping(NULL, 2);
/* Sleep 10 seconds. */
sleep(10);
}
return (0);
}

View File

@@ -1,60 +0,0 @@
# Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
include(CheckIncludeFiles)
add_executable(vtouchpad vtouchpad.c)
target_compile_definitions(vtouchpad PRIVATE DISABLE_VERBOSE)
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_sources(vtouchpad PRIVATE mouse_x11.c)
#
# If FreeBSD, add /usr/local/include to the search path too,
# see: https://wiki.freebsd.org/WarnerLosh/UsrLocal
#
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
set(CMAKE_REQUIRED_INCLUDES "/usr/local/include;/usr/include")
target_link_directories(vtouchpad PUBLIC /usr/local/lib)
target_include_directories(vtouchpad PUBLIC /usr/local/include)
endif()
CHECK_INCLUDE_FILES("xdo.h" HAVE_XDO_H)
if (NOT HAVE_XDO_H)
message(WARNING "libxdo/xdotool not found, trying to use XTest exts...")
CHECK_INCLUDE_FILES("X11/extensions/XTest.h" HAVE_XTEST_H)
if (NOT HAVE_XTEST_H)
message(WARNING "Unable to find XTest.h, vtouchpad is unable "
"to work in your system, disabling build...")
set_target_properties(vtouchpad
PROPERTIES EXCLUDE_FROM_ALL True)
endif ()
target_link_libraries(vtouchpad X11 Xtst ws pthread)
else()
target_compile_definitions(vtouchpad PUBLIC HAVE_XDOTOOL)
target_link_libraries(vtouchpad X11 xdo ws pthread)
endif()
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_sources(vtouchpad PRIVATE mouse_win.c)
target_link_libraries(vtouchpad ws ws2_32)
else()
message(WARNING "Operating System '${CMAKE_SYSTEM_NAME}' not supported!")
set_target_properties(vtouchpad
PROPERTIES EXCLUDE_FROM_ALL True)
endif ()

View File

@@ -1,50 +0,0 @@
# vtouchpad
This example implements a simple 'virtual touchpad' that allows a web client to control a remote computer's mouse via wsServer/websocket.
## Features
<p align="center">
<img align="center" src="https://i.imgur.com/sOTqwWI.png" alt="vtouchpad example">
<br>
vtouchpad example
</p>
The larger area represents the main touchpad, move the cursor over it to move the mouse. Left and right clicks within this area also work as expected.
The two smaller buttons represent the left and right buttons respectively.
## Building
Since mouse movement is OS-dependent, this example works on a limited number of systems, currently Linux, FreeBSD and Windows.
**Linux and FreeBSD builds:**
vtouchpad requires the environment to run under X11, and requires libX11, libXtst or libxdo (comes with xdotool).
A build for Ubuntu and the like:
```bash
$ sudo apt install libxdo-dev
$ cd wsServer/
$ mkdir build && cd build/
$ cmake ..
$ make
$ ./examples/vtouchpad/vtouchpad
```
FreeBSD (although xdotool is available, the package is currently without maintainer, so its use is discouraged; a normal build already works as expected):
```bash
$ cd wsServer/
$ mkdir build && cd build/
$ cmake ..
$ make
$ ./examples/vtouchpad/vtouchpad
```
**Windows builds:**
It does not require any special libraries, and should work on any version above Windows 2000.
```bash
$ cd wsServer/
$ mkdir build && cd build/
$ cmake .. -G "MinGW Makefiles"
$ mingw32-make
$ ./examples/vtouchpad/vtouchpad.exe
```

View File

@@ -1,141 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if !defined(_WIN32)
#error "Expected Windows here!"
#endif
#include <windows.h>
#include "vtouchpad.h"
/**
* @dir examples/vtouchpad
* @brief Touchpad example directory
*
* @file mouse_win.c
* @brief Mouse windows implementation.
*/
/**
* Stub structure.
*/
struct mouse { int stub; };
/**
* @brief Do nothing, stub.
*
* @return Returns always 0.
*/
mouse_t *mouse_new(void)
{
return (NULL);
}
/**
* @brief Do nothing, stub.
*
* @param mouse Mouse structure pointer.
*
* @return Returns always NULL.
*/
void *mouse_free(mouse_t *mouse)
{
((void)mouse);
return (NULL);
}
/**
* @brief Moves the mouse pointed by @p mouse to the
* offsets @p x_off and @p y_off.
*
* @param mouse Mouse structure pointer.
* @param x_off X-coordinate offset.
* @param y_off Y-coordinate offset.
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_move_relative(mouse_t *mouse, int x_off, int y_off)
{
((void)mouse);
int ret;
INPUT input = {0};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
input.mi.dx = x_off;
input.mi.dy = y_off;
ret = SendInput(1, &input, sizeof(input));
return (ret == 0);
}
/**
* @brief Makes a 'button press' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was pressed (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_down(mouse_t *mouse, int button)
{
((void)mouse);
int ret;
INPUT input = {0};
if (button == MOUSE_BTN_LEFT)
button = MOUSEEVENTF_LEFTDOWN;
else
button = MOUSEEVENTF_RIGHTDOWN;
input.type = INPUT_MOUSE;
input.mi.dwFlags = button;
ret = SendInput(1, &input, sizeof(input));
return (ret == 0);
}
/**
* @brief Makes a 'button release' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was released (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_up(mouse_t *mouse, int button)
{
((void)mouse);
int ret;
INPUT input = {0};
if (button == MOUSE_BTN_LEFT)
button = MOUSEEVENTF_LEFTUP;
else
button = MOUSEEVENTF_RIGHTUP;
input.type = INPUT_MOUSE;
input.mi.dwFlags = button;
ret = SendInput(1, &input, sizeof(input));
return (ret == 0);
}

View File

@@ -1,179 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if !defined(__linux__) && !defined(__FreeBSD__)
#error "Expected Linux or FreeBSD here!"
#endif
#include <X11/Xlib.h>
#ifdef HAVE_XDOTOOL
#include <xdo.h>
#else
#include <X11/extensions/XTest.h>
#endif
#include "vtouchpad.h"
/**
* @dir examples/vtouchpad
* @brief Touchpad example directory
*
* @file mouse_x11.c
* @brief Mouse X11 implementation.
*/
/**
* Note:
* Although the use of libxdo/xdotool seems unnecessary (since libxdo *also*
* uses X11's XTest extension), I believe it can still be interesting: the
* xdotool implementation may change in the future, as well as support new
* systems, so dealing with libxdo seems to be better than using X11 directly.
*
* Link:
* If libxdo present: -lX11 -lxdo
* Else : -lX11 -lXtst
*
*/
/**
* Mouse pointer, hold OS-dependent data structures.
*/
struct mouse
{
#ifdef HAVE_XDOTOOL
xdo_t *xdo;
#else
Display *dpy;
Window root;
#endif
};
/**
* @brief Allocates a new mouse data structure.
*
* @return Returns a new mouse_t pointer object.
*/
mouse_t *mouse_new(void)
{
struct mouse *mouse;
mouse = calloc(1, sizeof(struct mouse));
if (!mouse)
return (NULL);
#ifdef HAVE_XDOTOOL
mouse->xdo = xdo_new(NULL);
if (!mouse->xdo)
return mouse_free(mouse);
#else
mouse->dpy = XOpenDisplay(0);
if (!mouse->dpy)
return mouse_free(mouse);
mouse->root = XRootWindow(mouse->dpy, DefaultScreen(mouse->dpy));
#endif
return (mouse);
}
/**
* @brief Frees a previously allocated mouse_t pointer.
*
* @return Always NULL.
*/
void *mouse_free(mouse_t *mouse)
{
if (!mouse)
goto out;
#ifdef HAVE_XDOTOOL
if (mouse->xdo)
xdo_free(mouse->xdo);
#else
if (mouse->dpy)
XCloseDisplay(mouse->dpy);
#endif
free(mouse);
out:
return (NULL);
}
/**
* @brief Moves the mouse pointed by @p mouse to the
* offsets @p x_off and @p y_off.
*
* @param mouse Mouse structure pointer.
* @param x_off X-coordinate offset.
* @param y_off Y-coordinate offset.
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_move_relative(mouse_t *mouse, int x_off, int y_off)
{
#ifdef HAVE_XDOTOOL
return xdo_move_mouse_relative(mouse->xdo, x_off, y_off);
#else
int ret;
ret = XTestFakeRelativeMotionEvent(mouse->dpy, x_off, y_off, CurrentTime);
XFlush(mouse->dpy);
return (ret == 0);
#endif
}
/**
* @brief Makes a 'button press' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was pressed (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_down(mouse_t *mouse, int button)
{
#ifdef HAVE_XDOTOOL
return xdo_mouse_down(mouse->xdo, CURRENTWINDOW, button);
#else
int ret;
ret = XTestFakeButtonEvent(mouse->dpy, button, 1, CurrentTime);
XFlush(mouse->dpy);
return (ret == 0);
#endif
}
/**
* @brief Makes a 'button release' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was released (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_up(mouse_t *mouse, int button)
{
#ifdef HAVE_XDOTOOL
return xdo_mouse_up(mouse->xdo, CURRENTWINDOW, button);
#else
int ret;
ret = XTestFakeButtonEvent(mouse->dpy, button, 0, CurrentTime);
XFlush(mouse->dpy);
return (ret == 0);
#endif
}

View File

@@ -1,210 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>
#include "vtouchpad.h"
/**
* @dir examples/vtouchpad
* @brief Touchpad example directory
*
* @file vtouchpad.c
* @brief Main file.
*/
/* Mouse global structure. */
mouse_t *mouse;
/**
* @brief Given a string @p ev containing a single event,
* parses it as expected.
*
* The event can be (D stands to 'delimiter', currently ';'):
* (Mouse Movement): mouse_move D off_X D off_Y, where the offset is an
* integer type that represents the amount of pixels
* the mouse have to move. Example: mouse_move;10;-20.
*
* Negative offsets represents movement to the left and
* to the top. Positive offsets are the opposite.
*
* (Mouse Button Press): Represents if a mouse button has been pressed or
* released.
*
* Valid events are:
* a) mouse_btn_left_down (left button press)
* b) mouse_btn_left_up (left button release)
* c) mouse_btn_right_down
* d) mouse_btn_right_up
*
* @param ev String representing the event.
* @param error Set to 1 if an error was found, 0 otherwise.
*
* @returns Returns a mouse_event structure.
*/
struct mouse_event parse_event(const char *ev, int *error)
{
struct mouse_event mev = {0};
char str[128] = {0};
char *saveptr, *s;
*error = 1;
saveptr = NULL;
strncpy(str, ev, sizeof(str) - 1);
s = strtok_r(str, EVENT_DELIMITER, &saveptr);
if (!strcmp(s, "mouse_move"))
mev.event = MOUSE_MOVE;
else if (!strcmp(s, "mouse_btn_left_down"))
mev.event = MOUSE_BTN_LEFT_DOWN;
else if (!strcmp(s, "mouse_btn_left_up"))
mev.event = MOUSE_BTN_LEFT_UP;
else if (!strcmp(s, "mouse_btn_right_down"))
mev.event = MOUSE_BTN_RIGHT_DOWN;
else if (!strcmp(s, "mouse_btn_right_up"))
mev.event = MOUSE_BTN_RIGHT_UP;
else
{
fprintf(stderr, "Unknown event: (%s)\n", s);
goto out0;
}
if (mev.event != MOUSE_MOVE)
goto out1;
/* X offset. */
s = strtok_r(NULL, EVENT_DELIMITER, &saveptr);
if (!s)
{
fprintf(stderr, "A movement event requires a X/Y offsets, X not found!\n");
goto out0;
}
mev.x_off = atoi(s);
/* Y offset. */
s = strtok_r(NULL, EVENT_DELIMITER, &saveptr);
if (!s)
{
fprintf(stderr, "A movement event requires a X/Y offsets, Y not found!\n");
goto out0;
}
mev.y_off = atoi(s);
out1:
*error = 0;
out0:
return (mev);
}
/**
* @brief On open event, just signals that a new client has
* been connected.
*
* @param client Client connection.
*/
void onopen(ws_cli_conn_t *client) {
(void)client;
printf("Connected!\n");
}
/**
* @brief On close event, just signals that a client has
* been disconnected.
*
* @param client Client connection.
*/
void onclose(ws_cli_conn_t *client) {
(void)client;
printf("Disconnected!\n");
}
/**
* @brief For each new event, parses the string as expected all call
* the appropriate routines.
*
* @param client Client connection. (ignored)
*
* @param msg Received message/event.
*
* @param size Message size (in bytes). (ignored)
*
* @param type Message type. (ignored)
*/
void onmessage(ws_cli_conn_t *client,
const unsigned char *msg, uint64_t size, int type)
{
((void)client);
((void)size);
((void)type);
struct mouse_event mev;
int err;
/* Parse the event. */
mev = parse_event((const char *)msg, &err);
if (err)
return;
/* Call the appropriate routine. */
switch (mev.event)
{
case MOUSE_MOVE:
VTOUCH_DEBUG(("move: %d / %d\n", mev.x_off, mev.y_off));
mouse_move_relative(mouse, mev.x_off, mev.y_off);
break;
case MOUSE_BTN_LEFT_DOWN:
VTOUCH_DEBUG(("mouse left down\n"));
mouse_down(mouse, MOUSE_BTN_LEFT);
break;
case MOUSE_BTN_LEFT_UP:
VTOUCH_DEBUG(("mouse left up\n"));
mouse_up(mouse, MOUSE_BTN_LEFT);
break;
case MOUSE_BTN_RIGHT_DOWN:
VTOUCH_DEBUG(("mouse right down\n"));
mouse_down(mouse, MOUSE_BTN_RIGHT);
break;
case MOUSE_BTN_RIGHT_UP:
VTOUCH_DEBUG(("mouse right up\n"));
mouse_up(mouse, MOUSE_BTN_RIGHT);
break;
}
}
/* Main routine. */
int main(void)
{
struct ws_events evs;
/* Register events. */
evs.onopen = &onopen;
evs.onclose = &onclose;
evs.onmessage = &onmessage;
/* Mouse. */
mouse = mouse_new();
ws_socket(&evs, 8080, 0, 1000);
mouse_free(mouse);
return (0);
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef VTOUCHPAD_H
#define VTOUCHPAD_H
/* Debug. */
#ifndef DISABLE_VERBOSE
#define VTOUCH_DEBUG(x) printf x
#else
#define VTOUCH_DEBUG(x)
#endif
/* Mouse definitions. */
#define MOUSE_MOVE 1
#define MOUSE_BTN_LEFT_DOWN 2
#define MOUSE_BTN_LEFT_UP 4
#define MOUSE_BTN_RIGHT_DOWN 8
#define MOUSE_BTN_RIGHT_UP 16
#define EVENT_DELIMITER "; "
/* Mouse buttons. */
#define MOUSE_BTN_LEFT 1
#define MOUSE_BTN_RIGHT 3
/* Mouse event structure. */
struct mouse_event
{
int event;
int x_off;
int y_off;
};
/* Opaque type to 'struct mouse'. */
typedef struct mouse mouse_t;
/* External declarations. */
extern mouse_t *mouse_new(void);
extern void* mouse_free(mouse_t *mouse);
extern int mouse_move_relative(mouse_t *mouse, int x_off, int y_off);
extern int mouse_down(mouse_t *mouse, int button);
extern int mouse_up(mouse_t *mouse, int button);
#endif /* MAIN_H */

View File

@@ -1,227 +0,0 @@
<!--
Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
-->
<html>
<head>
<script>
let ws;
let x = 0;
let y = 0;
let move_counter = 0;
let is_connected = false;
const THRESHOLD = 1;
const DEBUG = true;
const DELIMITER = ";";
function get_touch(e) {
const evt = (typeof e.originalEvent === 'undefined') ?
e : e.originalEvent;
const touch = evt.touches[0] || evt.changedTouches[0];
return (touch);
}
function is_touch_device() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
function fix_num(n) {
return (n == NaN ? 0 : Math.round(n));
}
window.addEventListener("load", function () {
/* Mouse events. */
const touch = document.getElementById('touch');
touch.addEventListener('mousemove', e => {
if (is_touch_device())
return;
mouse_move(e.offsetX, e.offsetY);
});
touch.addEventListener('mousedown', e => {
mouse_down(e);
});
touch.addEventListener('mouseup', e => {
mouse_up(e);
});
touch.addEventListener('mouseenter', e => {
x = e.offsetX;
y = e.offsetY;
});
/* Touch events. */
touch.addEventListener('touchmove', e => {
const touch = get_touch(e);
mouse_move(fix_num(touch.pageX), fix_num(touch.pageY));
});
touch.addEventListener('touchstart', e => {
const touch = get_touch(e);
x = fix_num(touch.pageX);
y = fix_num(touch.pageY);
});
/* Button left. */
const btn_left = document.getElementById('btn_left');
btn_left.addEventListener('mousedown', e => {
mouse_down(e, "mouse_btn_left_down");
});
btn_left.addEventListener('mouseup', e => {
mouse_up(e, "mouse_btn_left_up");
});
/* Button right. */
const btn_right = document.getElementById('btn_right');
btn_right.addEventListener('mousedown', e => {
mouse_down(e, "mouse_btn_right_down");
});
btn_right.addEventListener('mouseup', e => {
mouse_up(e, "mouse_btn_right_up");
});
/* Connect button. */
document.getElementById("bt_conn").onclick = function(){
const addr = document.getElementById("srv_addr").value;
toggle_connection(addr);
};
});
function toggle_connection(addr) {
if (is_connected) {
is_connected = false;
ws.close();
document.getElementById("bt_conn").value = "Disconnect!";
return;
}
ws = new WebSocket(addr);
ws.onopen = function(e) {
is_connected = true;
document.getElementById("bt_conn").value = "Disconnect!";
};
ws.onclose = function(e) {
is_connected = false;
document.getElementById("bt_conn").value = "Connect!";
};
}
function mouse_move(offX, offY) {
if (!is_connected)
return;
/* Do not trigger too much events.. we do not need
* to do 'micro movements' */
move_counter++;
if (move_counter != THRESHOLD)
return;
move_counter = 0;
const movX = offX - x;
const movY = offY - y;
x = offX;
y = offY;
ws.send("mouse_move" + DELIMITER + movX + DELIMITER + movY);
if (DEBUG)
console.log(movX + " / " + movY);
}
function mouse_down(e, btn) {
let event;
if (!is_connected)
return;
if (!btn) {
if (e.button == 0)
event = "mouse_btn_left_down";
else if (e.button == 2)
event = "mouse_btn_right_down";
else
return;
}
else
event = btn;
if (DEBUG)
console.log(event);
ws.send(event);
}
function mouse_up(e, btn) {
let event;
if (!is_connected)
return;
if (!btn) {
if (e.button == 0)
event = "mouse_btn_left_up";
else if (e.button == 2)
event = "mouse_btn_right_up";
else
return;
}
else
event = btn;
if (DEBUG)
console.log(event);
ws.send(event);
}
</script>
<style type="text/css">
body {
margin: 0 !important;
padding: 0 !important;
overscroll-behavior-y: contain;
}
#header { padding-top: 10px; padding-left: 5px; }
#touch {
background-image: linear-gradient(white, gray);
border: solid;
border-radius: 5px;
width: 80%;
height: 78%;
margin: 0 auto;
}
#wrapper { margin: 0 auto; width: 99%; text-align: center; }
#btn_left, #btn_right {
background-image: linear-gradient(white, gray);
border: solid;
border-radius: 5px;
width: 40%;
height: 11%;
display: inline-block;
margin-right: 10px;
margin-top: 10px;
}
#btn_right {
margin-right: 0;
}
</style>
</head>
<body oncontextmenu="return false;">
<div id="header">
Server: <input type="text" id="srv_addr" value="ws://192.168.X.Y:8080">
<input type="button" id="bt_conn" name="bt_conn" value="Connect!"><br /><br />
</div>
<div id="touch"></div>
<div id="wrapper">
<div id="btn_left"></div><div id="btn_right"></div>
</div>
</body>
</html>

View File

@@ -1,145 +0,0 @@
# ToyWS
Since there is some demand to support a client, 'ToyWS' is a response to those
requests: ToyWS is a toy WebSocket client, meaning that it's quite simple and
made to work (guaranteed) only with wsServer.
Limitations:
- Fixed handshake header
- Fixed frame mask (it should be random)
- No PING/PONG frame support
- No close handshake support: although it can identify CLOSE frames, it
does not send the response, only aborts the connection.
- No support for CONT frames, that is, the entire content of a frame (TXT
or BIN) must be contained within a single frame.
- Possibly other things too.
Although extremely limited, ToyWS was designed for those who want to _also_
have a C client that is lightweight and compatible with wsServer, thus,
freeing the need for a browser and/or third-party libraries to test and use
wsServer.
Maybe this client will evolve into something more complete and general in the
future, but that's not in the roadmap at the moment.
## API
The API is quite simple and is summarized in 4 routines, to connect,
disconnect, send and receive frame, as follows:
```c
int tws_connect(struct tws_ctx *ctx, const char *ip, uint16_t port);
```
Connect to a given `ip` address and `port`.
**Return**:
Returns a positive number if success, otherwise, a negative number.
**Note:**
`struct tws_ctx *ctx` is for internal usage and initialized within this
function. There is no need to access this structure or modify its values, ToyWS
just needs it to maintain the consistent client state.
---
```c
void tws_close(struct tws_ctx *ctx);
```
Close the connection for the given `ctx`.
---
```c
int tws_sendframe(struct tws_ctx *ctx, uint8_t *msg, uint64_t size, int type);
```
Send a frame of type `type` with content `msg` and size `size` for a given
context `ctx`.
Valid frame types are:
- FRM_TXT
- FRM_BIN
**Return**:
Returns 0 if success, otherwise, a negative number.
---
```c
int tws_receiveframe(struct tws_ctx *ctx, char **buff, size_t *buff_size,
int *frm_type);
```
Receive a frame and save it on `buff`.
**Parameters:**
**`buff`:**
Pointer to the target buffer. If NULL, ToyWS will allocate a new buffer that is
capable to hold the frame and save into `buff`.
If already exists: the function will try to fill the buffer with the frame
content, if the frame size is bigger than `buff_size`, the function will
reallocate `buff` and update `buff_size` with the new size.
**`buff_size`:**
Current buffer size. __Must__ point the a valid memory region. If `*buff`
points to NULL, `*buff_size` must be equals to 0.
**`frm_type`:**
Frame type read. The frame type received will be reflected into the contents of
this pointer.
**Return**: Returns 0 if success, a negative number otherwise.
**Note**:
- This routine is blocking, that is, it will only return if it manages to read
a frame or if there is an error during the reading (such as invalid (or
unsupported) frame or server disconnection).
- At the end of everything, don't forget to free the buffer!. Once its size is
relocated, a single call to 'free' is sufficient.
## Example
The example below illustrates the usage (also available at (extra/toyws/tws_test.c)):
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "toyws.h"
int main(void)
{
struct tws_ctx ctx;
char msg[] = "Hello";
/* Buffer/frame params. */
char *buff;
int frm_type;
size_t buff_size;
buff = NULL;
buff_size = 0;
frm_type = 0;
if (tws_connect(&ctx, "127.0.0.1", 8080) < 0)
fprintf(stderr, "Unable to connect!\n");
/* Send message. */
printf("Send: %s\n",
(tws_sendframe(&ctx, msg, strlen(msg), FRM_TXT) >= 0 ?
"Success" : "Failed"));
/* Blocks until receive a single message. */
if (tws_receiveframe(&ctx, &buff, &buff_size, &frm_type) < 0)
fprintf(stderr, "Unable to receive message!\n");
printf("I received: (%s) (type: %s)\n", buff,
(frm_type == FRM_TXT ? "Text" : "Binary"));
tws_close(&ctx);
free(buff);
return (0);
}
```

View File

@@ -1,410 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <inttypes.h>
#include <unistd.h>
#ifndef _WIN32
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
typedef unsigned long in_addr_t;
#endif
/* Windows and macOS seems to not have MSG_NOSIGNAL */
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#include "toyws.h"
/*
* This is a WebSocket 'toy client', made exclusively to work with wsServer.
*
* Although it is independent of the wsServer src tree, it is highly limited
* and therefore not recommended for use with other servers.
*
* There are the following restrictions (not limited to):
* - Fixed handshake header
*
* - Fixed frame mask (ideally it should be random)
*
* - No PING/PONG frame support
*
* - No close handshake support: although it can identify CLOSE frames, it
* does not send the response, it just aborts the connection.
*
* - No support for CONT frames, that is, the entire content of a frame (TXT
* or BIN) must be contained in a single message.
*
* - Possibly other things too.
*
* Other than that, this client supports sending and receiving frames and
* should work 'ok' with wsServer. Since there was some demand for client
* support, this mini-project is a response to those requests =).
*/
/* Dummy/constant request. */
static const char request[] =
"GET / HTTP/1.1\r\n"
"Host: localhost:8080\r\n"
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: uaGPoPbZRzHcWDXiNQ5dyg==\r\n\r\n";
/**
* @brief Connect to a given @p ip address and @p port.
*
* @param ctx WebSocket client context.
* @param ip Target IP address to connect.
* @param port Server port.
*
* @return Returns the socket fd if success, otherwise, returns
* a negative number.
*/
int tws_connect(struct tws_ctx *ctx, const char *ip, uint16_t port)
{
char *p;
int sock;
ssize_t ret;
in_addr_t ip_addr;
struct sockaddr_in sock_addr;
memset(ctx, 0, sizeof(*ctx));
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
fprintf(stderr, "WSAStartup failed!");
return (-1);
}
/**
* Sets stdout to be non-buffered.
*
* According to the docs from MSDN (setvbuf page), Windows do not
* really supports line buffering but full-buffering instead.
*
* Quote from the docs:
* "... _IOLBF For some systems, this provides line buffering.
* However, for Win32, the behavior is the same as _IOFBF"
*/
setvbuf(stdout, NULL, _IONBF, 0);
#endif
/* Create socket. */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
return (-1);
memset((void*)&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
if ((ip_addr = inet_addr(ip)) == INADDR_NONE)
return (-1);
sock_addr.sin_addr.s_addr = ip_addr;
sock_addr.sin_port = htons(port);
/* Connect. */
if (connect(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0)
return (-1);
/* Do handhshake. */
if (send(sock, request, strlen(request), MSG_NOSIGNAL) < 0)
return (-1);
/* Wait for 'switching protocols'. */
if ((ret = recv(sock, ctx->frm, sizeof(ctx->frm), 0)) < 0)
return (-1);
/* Advance our pointers before the first next_byte(). */
p = strstr((const char *)ctx->frm, "\r\n\r\n");
ctx->amt_read = ret;
ctx->cur_pos = (size_t)((ptrdiff_t)(p - (char *)ctx->frm)) + 4;
ctx->fd = sock;
ctx->status = TWS_ST_CONNECTED;
return (sock);
}
/**
* @brief Close the connection for the given @p ctx.
*
* @param ctx WebSocket client context.
*/
void tws_close(struct tws_ctx *ctx)
{
if (ctx->status == TWS_ST_DISCONNECTED)
return;
#ifndef _WIN32
shutdown(ctx->fd, SHUT_RDWR);
close(ctx->fd);
#else
closesocket(ctx->fd);
WSACleanup();
#endif
ctx->status = TWS_ST_DISCONNECTED;
}
/**
* @brief Send a frame of type @p type with content @p msg and
* size @p size for a given context @p ctx.
*
* @param ctx WebSocket client context.
* @param msg Frame message.
* @param size Frame size.
* @param type Frame type (e.g: FRM_TXT, FRM_BIN...)
*
* @return Returns 0 if success, a negative number otherwise.
*/
int tws_sendframe(struct tws_ctx *ctx, uint8_t *msg, uint64_t size,
int type)
{
uint8_t frame[10] = {0};
uint8_t masks[4];
uint64_t length;
uint8_t hdr_len;
size_t count;
uint8_t *tmp;
uint8_t *p;
frame[0] = FRM_FIN | type;
frame[1] |= FRM_MSK;
length = (uint64_t)size;
/* Split the size between octets. */
if (length <= 125)
{
frame[1] |= length & 0x7F;
hdr_len = 2;
}
/* Size between 126 and 65535 bytes. */
else if (length >= 126 && length <= 65535)
{
frame[1] |= 126;
frame[2] = (length >> 8) & 255;
frame[3] = length & 255;
hdr_len = 4;
}
/* More than 65535 bytes. */
else
{
frame[1] |= 127;
frame[2] = (uint8_t)((length >> 56) & 255);
frame[3] = (uint8_t)((length >> 48) & 255);
frame[4] = (uint8_t)((length >> 40) & 255);
frame[5] = (uint8_t)((length >> 32) & 255);
frame[6] = (uint8_t)((length >> 24) & 255);
frame[7] = (uint8_t)((length >> 16) & 255);
frame[8] = (uint8_t)((length >> 8) & 255);
frame[9] = (uint8_t)(length & 255);
hdr_len = 10;
}
/* Send header. */
if (send(ctx->fd, frame, hdr_len, MSG_NOSIGNAL) < 0)
return (-1);
/* Send dummy masks. */
masks[0] = masks[1] = masks[2] = masks[3] = 0xAA;
if (send(ctx->fd, masks, 4, MSG_NOSIGNAL) < 0)
return (-2);
/* Mask message and send it. */
p = calloc(1, size);
if (!p)
return (-3);
memcpy(p, msg, size);
for (tmp = p, count = 0; count < size; tmp++, count++)
*tmp ^= masks[count % 4];
if (send(ctx->fd, p, size, MSG_NOSIGNAL) < 0)
{
free(p);
return (-4);
}
free(p);
return (0);
}
/**
* @brief Read a chunk of bytes and return the next byte
* belonging to the frame.
*
* @param ctx Websocket Context.
* @param ret Optional return code.
*
* @return Returns the byte read, or -1 if error.
*/
static inline int next_byte(struct tws_ctx *ctx, int *ret)
{
ssize_t n;
/* If empty or full. */
if (ctx->cur_pos == 0 || ctx->cur_pos == ctx->amt_read)
{
if ((n = recv(ctx->fd, ctx->frm, sizeof(ctx->frm), 0)) <= 0)
{
if (ret)
*ret = 1;
return (-1);
}
ctx->amt_read = (size_t)n;
ctx->cur_pos = 0;
}
return (ctx->frm[ctx->cur_pos++]);
}
/**
* @brief Skips @p frame_size bytes of the current frame.
*
* @param ctx Websocket Context.
* @param frame_size Amount of bytes to be skipped.
*
* @return Returns 0 if success, a negative number
* otherwise.
*/
static int skip_frame(struct tws_ctx *ctx, uint64_t frame_size)
{
uint64_t i;
for (i = 0; i < frame_size; i++)
if (next_byte(ctx, NULL) == -1)
return (-1);
return (0);
}
/**
* @brief Receive a frame and save it on @p buff.
*
* If @p buff is NULL, this function will allocate a new
* buffer and save the new size in @p buff_size. If not
* NULL and if @p buff_size is greater than the frame,
* the buffer is used, otherwise, the address will be
* reallocated.
*
* @param ctx WebSocket Context.
* @param buff Buffer pointer.
* @param buff_size Buffer size.
* @param frm_type Frame type received.
*
* @return Returns 0 if success, a negative number
* otherwise.
*/
int tws_receiveframe(struct tws_ctx *ctx, char **buff,
size_t *buff_size, int *frm_type)
{
int ret;
char *buf;
uint64_t i;
int cur_byte;
uint8_t opcode;
uint64_t frame_length;
/* Buffer should be valid. */
if (!buff || (*buff && !buff_size))
return (-1);
ret = 0;
cur_byte = next_byte(ctx, &ret);
opcode = (cur_byte & 0xF);
/* If CLOSE, lets close, abruptly, because why not?. */
if (opcode == FRM_CLSE)
{
tws_close(ctx);
return (-1);
}
frame_length = next_byte(ctx, &ret) & 0x7F;
/* Read remaining length bytes, if any. */
if (frame_length == 126)
{
frame_length = (((uint64_t)next_byte(ctx, &ret)) << 8) |
next_byte(ctx, &ret);
}
else if (frame_length == 127)
{
frame_length =
(((uint64_t)next_byte(ctx, &ret)) << 56) | /* frame[2]. */
(((uint64_t)next_byte(ctx, &ret)) << 48) | /* frame[3]. */
(((uint64_t)next_byte(ctx, &ret)) << 40) |
(((uint64_t)next_byte(ctx, &ret)) << 32) |
(((uint64_t)next_byte(ctx, &ret)) << 24) |
(((uint64_t)next_byte(ctx, &ret)) << 16) |
(((uint64_t)next_byte(ctx, &ret)) << 8) |
(((uint64_t)next_byte(ctx, &ret))); /* frame[9]. */
}
/* Check if any error before proceed. */
if (ret)
return (ret);
/*
* If frame is something other than CLSE and TXT/BIN (like PONG
* or CONT), skip.
*/
if (opcode != FRM_TXT && opcode != FRM_BIN)
if (skip_frame(ctx, frame_length) < 0)
return (-1);
/* Allocate memory, if needed. */
if (*buff_size < frame_length)
{
buf = realloc(*buff, frame_length + 1);
if (!buf)
return (-1);
*buff = buf;
*buff_size = frame_length + 1;
}
else
buf = *buff;
/* Receive frame. */
for (i = 0; i < frame_length; i++, buf++)
{
cur_byte = next_byte(ctx, &ret);
if (cur_byte < 0)
return (ret);
*buf = cur_byte;
}
*buf = '\0';
/* Fill other infos. */
*frm_type = opcode;
return (ret);
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef TOYWS_H
#define TOYWS_H
#include <stdint.h>
/* Frame constants. */
#define FRM_TXT 1
#define FRM_BIN 2
#define FRM_CLSE 8
#define FRM_FIN 128
#define FRM_MSK 128
#define MESSAGE_LENGTH 1024
/* Client status. */
#define TWS_ST_DISCONNECTED 0
#define TWS_ST_CONNECTED 1
/* Client context. */
struct tws_ctx
{
uint8_t frm[MESSAGE_LENGTH];
size_t amt_read;
size_t cur_pos;
int status;
int fd;
};
/* External functions. */
extern int tws_connect(struct tws_ctx *ctx, const char *ip,
uint16_t port);
extern void tws_close(struct tws_ctx *ctx);
extern int tws_sendframe(struct tws_ctx *ctx, uint8_t *msg,
uint64_t size, int type);
extern int tws_receiveframe(struct tws_ctx *ctx, char **buff,
size_t *buff_size, int *frm_type);
#endif /* TOYWS_H */

View File

@@ -1,56 +0,0 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "toyws.h"
int main(void)
{
struct tws_ctx ctx;
char msg[] = "Hello";
/* Buffer params. */
char *buff;
int frm_type;
size_t buff_size;
buff = NULL;
buff_size = 0;
frm_type = 0;
if (tws_connect(&ctx, "127.0.0.1", 8080) < 0)
fprintf(stderr, "Unable to connect!\n");
/* Send message. */
printf("Send: %s\n",
(tws_sendframe(&ctx, (uint8_t*)msg, strlen(msg), FRM_TXT) >= 0 ?
"Success" : "Failed"));
/* Blocks until receive a single message. */
if (tws_receiveframe(&ctx, &buff, &buff_size, &frm_type) < 0)
fprintf(stderr, "Unable to receive message!\n");
printf("I received: (%s) (type: %s)\n", buff,
(frm_type == FRM_TXT ? "Text" : "Binary"));
tws_close(&ctx);
free(buff);
return (0);
}

View File

@@ -1,19 +0,0 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef BASE64_H
#define BASE64_H
#include <sys/types.h>
unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len);
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len);
#endif /* BASE64_H */

View File

@@ -1,73 +0,0 @@
/*
* sha1.h
*
* Description:
* This is the header file for code which implements the Secure
* Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
* April 17, 1995.
*
* Many of the variable names in this code, especially the
* single character names, were used because those were the names
* used in the publication.
*
* Please read the file sha1.c for more information.
*
*/
#ifndef _SHA1_H_
#define _SHA1_H_
#include <stdint.h>
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typdef the following:
* name meaning
* uint32_t unsigned 32 bit integer
* uint8_t unsigned 8 bit integer (i.e., unsigned char)
* int_least16_t integer of >= 16 bits
*
*/
#ifndef _SHA_enum_
#define _SHA_enum_
enum
{
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError /* called Input after Result */
};
#endif
#define SHA1HashSize 20
/*
* This structure will hold context information for the SHA-1
* hashing operation
*/
typedef struct SHA1Context
{
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
uint32_t Length_Low; /* Message length in bits */
uint32_t Length_High; /* Message length in bits */
/* Index into message block array */
int_least16_t Message_Block_Index;
uint8_t Message_Block[64]; /* 512-bit message blocks */
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the message digest corrupted? */
} SHA1Context;
/*
* Function Prototypes
*/
int SHA1Reset( SHA1Context *);
int SHA1Input( SHA1Context *,
const uint8_t *,
unsigned int);
int SHA1Result( SHA1Context *,
uint8_t Message_Digest[SHA1HashSize]);
#endif

View File

@@ -1,37 +0,0 @@
/**
* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
*
* 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.
*/
#ifndef UTF8_DECODE_H
#define UTF8_DECODE_H
#include <inttypes.h>
#include <stddef.h>
/* UTF8 return state. */
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
extern int is_utf8(uint8_t* s);
extern int is_utf8_len(uint8_t *s, size_t len);
extern uint32_t is_utf8_len_state(uint8_t *s, size_t len, uint32_t state);
#endif

View File

@@ -1,281 +0,0 @@
/*
* Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @dir include/
* @brief wsServer include directory
*
* @file ws.h
* @brief wsServer constants and functions.
*/
#ifndef WS_H
#define WS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
/**
* @name Global configurations
*/
/**@{*/
/**
* @brief Max clients connected simultaneously.
*/
#ifndef MAX_CLIENTS
#define MAX_CLIENTS 8
#endif
/**
* @name Key and message configurations.
*/
/**@{*/
/**
* @brief Message buffer length.
*/
#define MESSAGE_LENGTH 2048
/**
* @brief Maximum frame/message length.
*/
#define MAX_FRAME_LENGTH (16*1024*1024)
/**
* @brief WebSocket key length.
*/
#define WS_KEY_LEN 24
/**
* @brief Magic string length.
*/
#define WS_MS_LEN 36
/**
* @brief Accept message response length.
*/
#define WS_KEYMS_LEN (WS_KEY_LEN + WS_MS_LEN)
/**
* @brief Magic string.
*/
#define MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
/**@}*/
/**
* @name Handshake constants.
*/
/**@{*/
/**
* @brief Alias for 'Sec-WebSocket-Key'.
*/
#define WS_HS_REQ "Sec-WebSocket-Key"
/**
* @brief Handshake accept message length.
*/
#define WS_HS_ACCLEN 130
/**
* @brief Handshake accept message.
*/
#define WS_HS_ACCEPT \
"HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade: websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: "
/**@}*/
/**
* @name Frame types.
*/
/**@{*/
/**
* @brief Frame FIN.
*/
#define WS_FIN 128
/**
* @brief Frame FIN shift
*/
#define WS_FIN_SHIFT 7
/**
* @brief Continuation frame.
*/
#define WS_FR_OP_CONT 0
/**
* @brief Text frame.
*/
#define WS_FR_OP_TXT 1
/**
* @brief Binary frame.
*/
#define WS_FR_OP_BIN 2
/**
* @brief Close frame.
*/
#define WS_FR_OP_CLSE 8
/**
* @brief Ping frame.
*/
#define WS_FR_OP_PING 0x9
/**
* @brief Pong frame.
*/
#define WS_FR_OP_PONG 0xA
/**
* @brief Unsupported frame.
*/
#define WS_FR_OP_UNSUPPORTED 0xF
/**@}*/
/**
* @name Close codes
*/
/**@{*/
/**
* @brief Normal close
*/
#define WS_CLSE_NORMAL 1000
/**
* @brief Protocol error
*/
#define WS_CLSE_PROTERR 1002
/**@}*/
/**
* @brief Inconsistent message (invalid utf-8)
*/
#define WS_CLSE_INVUTF8 1007
/**
* @name Connection states
*/
/**@{*/
/**
* @brief Connection not established yet.
*/
#define WS_STATE_CONNECTING 0
/**
* @brief Communicating.
*/
#define WS_STATE_OPEN 1
/**
* @brief Closing state.
*/
#define WS_STATE_CLOSING 2
/**
* @brief Closed.
*/
#define WS_STATE_CLOSED 3
/**@}*/
/**
* @name Timeout util
*/
/**@{*/
/**
* @brief Nanoseconds macro converter
*/
#define MS_TO_NS(x) ((x)*1000000)
/**
* @brief Timeout in milliseconds.
*/
#define TIMEOUT_MS (500)
/**@}*/
/**
* @name Handshake constants.
*/
/**@{*/
/**
* @brief Debug
*/
#ifdef VERBOSE_MODE
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#else
#define DEBUG(...)
#endif
/**@}*/
#ifndef AFL_FUZZ
#define SEND(client,buf,len) send_all((client), (buf), (len), MSG_NOSIGNAL)
#define RECV(fd,buf,len) recv((fd)->client_sock, (buf), (len), 0)
#else
#define SEND(client,buf,len) write(fileno(stdout), (buf), (len))
#define RECV(fd,buf,len) read((fd)->client_sock, (buf), (len))
#endif
/* Opaque client connection type. */
typedef struct ws_connection ws_cli_conn_t;
/**
* @brief events Web Socket events types.
*/
struct ws_events
{
/**
* @brief On open event, called when a new client connects.
*/
void (*onopen)(ws_cli_conn_t *client);
/**
* @brief On close event, called when a client disconnects.
*/
void (*onclose)(ws_cli_conn_t *client);
/**
* @brief On message event, called when a client sends a text
* or binary message.
*/
void (*onmessage)(ws_cli_conn_t *client,
const unsigned char *msg, uint64_t msg_size, int type);
};
/* Forward declarations. */
/* Internal usage. */
extern int get_handshake_accept(char *wsKey, unsigned char **dest);
extern int get_handshake_response(char *hsrequest, char **hsresponse);
/* External usage. */
extern char *ws_getaddress(ws_cli_conn_t *client);
extern int ws_sendframe(
ws_cli_conn_t *cli, const char *msg, uint64_t size, int type);
extern int ws_sendframe_txt(ws_cli_conn_t *cli, const char *msg);
extern int ws_sendframe_bin(ws_cli_conn_t *cli, const char *msg, uint64_t size);
extern int ws_get_state(ws_cli_conn_t *cli);
extern int ws_close_client(ws_cli_conn_t *cli);
extern int ws_socket(struct ws_events *evs, uint16_t port, int thread_loop,
uint32_t timeout_ms);
/* Ping routines. */
extern void ws_ping(ws_cli_conn_t *cli, int threshold);
#ifdef AFL_FUZZ
extern int ws_file(struct ws_events *evs, const char *file);
#endif
#ifdef __cplusplus
}
#endif
#endif /* WS_H */

View File

@@ -1,155 +0,0 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <base64.h>
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* base64_encode - Base64 encode
* @src: Data to be encoded
* @len: Length of the data to be encoded
* @out_len: Pointer to output length variable, or %NULL if not used
* Returns: Allocated buffer of out_len bytes of encoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer. Returned buffer is
* nul terminated to make it easier to use as a C string. The nul terminator is
* not included in out_len.
*/
unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len)
{
unsigned char *out, *pos;
const unsigned char *end, *in;
size_t olen;
int line_len;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len)
return NULL; /* integer overflow */
out = malloc(olen);
if (out == NULL)
return NULL;
end = src + len;
in = src;
pos = out;
line_len = 0;
while (end - in >= 3) {
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
line_len += 4;
if (line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
}
if (end - in) {
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1) {
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
} else {
*pos++ = base64_table[((in[0] & 0x03) << 4) |
(in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
line_len += 4;
}
if (line_len)
*pos++ = '\n';
*pos = '\0';
if (out_len)
*out_len = pos - out;
return out;
}
/**
* base64_decode - Base64 decode
* @src: Data to be decoded
* @len: Length of the data to be decoded
* @out_len: Pointer to output length variable
* Returns: Allocated buffer of out_len bytes of decoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer.
*/
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len)
{
unsigned char dtable[256], *out, *pos, block[4], tmp;
size_t i, count, olen;
int pad = 0;
memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++)
dtable[base64_table[i]] = (unsigned char) i;
dtable['='] = 0;
count = 0;
for (i = 0; i < len; i++) {
if (dtable[src[i]] != 0x80)
count++;
}
if (count == 0 || count % 4)
return NULL;
olen = count / 4 * 3;
pos = out = malloc(olen);
if (out == NULL)
return NULL;
count = 0;
for (i = 0; i < len; i++) {
tmp = dtable[src[i]];
if (tmp == 0x80)
continue;
if (src[i] == '=')
pad++;
block[count] = tmp;
count++;
if (count == 4) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
if (pad) {
if (pad == 1)
pos--;
else if (pad == 2)
pos -= 2;
else {
/* Invalid padding */
free(out);
return NULL;
}
break;
}
}
}
*out_len = pos - out;
return out;
}

View File

@@ -1,124 +0,0 @@
/*
* Copyright (C) 2016-2020 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#define _POSIX_C_SOURCE 200809L
#include <base64.h>
#include <sha1.h>
#include <ws.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* @dir src/
* @brief Handshake routines directory
*
* @file handshake.c
* @brief Handshake routines.
*/
/**
* @brief Gets the field Sec-WebSocket-Accept on response, by
* an previously informed key.
*
* @param wsKey Sec-WebSocket-Key
* @param dest source to be stored the value.
*
* @return Returns 0 if success and a negative number
* otherwise.
*
* @attention This is part of the internal API and is documented just
* for completeness.
*/
int get_handshake_accept(char *wsKey, unsigned char **dest)
{
unsigned char hash[SHA1HashSize]; /* SHA-1 Hash. */
SHA1Context ctx; /* SHA-1 Context. */
char *str; /* WebSocket key + magic string. */
/* Invalid key. */
if (!wsKey)
return (-1);
str = calloc(1, sizeof(char) * (WS_KEY_LEN + WS_MS_LEN + 1));
if (!str)
return (-1);
strncpy(str, wsKey, WS_KEY_LEN);
strcat(str, MAGIC_STRING);
SHA1Reset(&ctx);
SHA1Input(&ctx, (const uint8_t *)str, WS_KEYMS_LEN);
SHA1Result(&ctx, hash);
*dest = base64_encode(hash, SHA1HashSize, NULL);
*(*dest + strlen((const char *)*dest) - 1) = '\0';
free(str);
return (0);
}
/**
* @brief Gets the complete response to accomplish a succesfully
* handshake.
*
* @param hsrequest Client request.
* @param hsresponse Server response.
*
* @return Returns 0 if success and a negative number
* otherwise.
*
* @attention This is part of the internal API and is documented just
* for completeness.
*/
int get_handshake_response(char *hsrequest, char **hsresponse)
{
unsigned char *accept; /* Accept message. */
char *saveptr; /* strtok_r() pointer. */
char *s; /* Current string. */
int ret; /* Return value. */
saveptr = NULL;
for (s = strtok_r(hsrequest, "\r\n", &saveptr); s != NULL;
s = strtok_r(NULL, "\r\n", &saveptr))
{
if (strstr(s, WS_HS_REQ) != NULL)
break;
}
/* Ensure that we have a valid pointer. */
if (s == NULL)
return (-1);
saveptr = NULL;
s = strtok_r(s, " ", &saveptr);
s = strtok_r(NULL, " ", &saveptr);
ret = get_handshake_accept(s, &accept);
if (ret < 0)
return (ret);
*hsresponse = malloc(sizeof(char) * WS_HS_ACCLEN);
if (*hsresponse == NULL)
return (-1);
strcpy(*hsresponse, WS_HS_ACCEPT);
strcat(*hsresponse, (const char *)accept);
strcat(*hsresponse, "\r\n\r\n");
free(accept);
return (0);
}

View File

@@ -1,389 +0,0 @@
/*
* sha1.c
*
* Description:
* This file implements the Secure Hashing Algorithm 1 as
* defined in FIPS PUB 180-1 published April 17, 1995.
*
* The SHA-1, produces a 160-bit message digest for a given
* data stream. It should take about 2**n steps to find a
* message with the same digest as a given message and
* 2**(n/2) to find any two messages with the same digest,
* when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code
* uses <stdint.h> (included via "sha1.h" to define 32 and 8
* bit unsigned integer types. If your C compiler does not
* support 32 bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. Although SHA-1 allows a message digest to be generated
* for messages of any number of bits less than 2^64, this
* implementation only works with messages with a length that is
* a multiple of the size of an 8-bit character.
*
*/
#include <sha1.h>
/*
* Define the SHA1 circular left shift macro
*/
#define SHA1CircularShift(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
/* Local Function Prototyptes */
void SHA1PadMessage(SHA1Context *);
void SHA1ProcessMessageBlock(SHA1Context *);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new SHA1 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Reset(SHA1Context *context)
{
if (!context)
{
return shaNull;
}
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = 0x67452301;
context->Intermediate_Hash[1] = 0xEFCDAB89;
context->Intermediate_Hash[2] = 0x98BADCFE;
context->Intermediate_Hash[3] = 0x10325476;
context->Intermediate_Hash[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
return shaSuccess;
}
/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 19th element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Result( SHA1Context *context,
uint8_t Message_Digest[SHA1HashSize])
{
int i;
if (!context || !Message_Digest)
{
return shaNull;
}
if (context->Corrupted)
{
return context->Corrupted;
}
if (!context->Computed)
{
SHA1PadMessage(context);
for(i=0; i<64; ++i)
{
/* message may be sensitive, clear it out */
context->Message_Block[i] = 0;
}
context->Length_Low = 0; /* and clear length */
context->Length_High = 0;
context->Computed = 1;
}
for(i = 0; i < SHA1HashSize; ++i)
{
Message_Digest[i] = context->Intermediate_Hash[i>>2]
>> 8 * ( 3 - ( i & 0x03 ) );
}
return shaSuccess;
}
/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/
int SHA1Input( SHA1Context *context,
const uint8_t *message_array,
unsigned length)
{
if (!length)
{
return shaSuccess;
}
if (!context || !message_array)
{
return shaNull;
}
if (context->Computed)
{
context->Corrupted = shaStateError;
return shaStateError;
}
if (context->Corrupted)
{
return context->Corrupted;
}
while(length-- && !context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] =
(*message_array & 0xFF);
context->Length_Low += 8;
if (context->Length_Low == 0)
{
context->Length_High++;
if (context->Length_High == 0)
{
/* Message is too long */
context->Corrupted = 1;
}
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
return shaSuccess;
}
/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the publication.
*
*
*/
void SHA1ProcessMessageBlock(SHA1Context *context)
{
const uint32_t K[] = { /* Constants defined in SHA-1 */
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
uint32_t temp; /* Temporary word value */
uint32_t W[80]; /* Word sequence */
uint32_t A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for(t = 0; t < 16; t++)
{
W[t] = context->Message_Block[t * 4] << 24;
W[t] |= context->Message_Block[t * 4 + 1] << 16;
W[t] |= context->Message_Block[t * 4 + 2] << 8;
W[t] |= context->Message_Block[t * 4 + 3];
}
for(t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Message_Block_Index = 0;
}
/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call the ProcessMessageBlock function
* provided appropriately. When it returns, it can be assumed that
* the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
* ProcessMessageBlock: [in]
* The appropriate SHA*ProcessMessageBlock function
* Returns:
* Nothing.
*
*/
void SHA1PadMessage(SHA1Context *context)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 64)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = context->Length_High >> 24;
context->Message_Block[57] = context->Length_High >> 16;
context->Message_Block[58] = context->Length_High >> 8;
context->Message_Block[59] = context->Length_High;
context->Message_Block[60] = context->Length_Low >> 24;
context->Message_Block[61] = context->Length_Low >> 16;
context->Message_Block[62] = context->Length_Low >> 8;
context->Message_Block[63] = context->Length_Low;
SHA1ProcessMessageBlock(context);
}

View File

@@ -1,88 +0,0 @@
/**
* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
*
* 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.
*/
/*
* Amazing utf8 decoder & validator grabbed from:
* http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
*
* All rights goes to the original author.
*/
#include "utf8.h"
static const uint8_t utf8d[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
};
static
uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
uint32_t type = utf8d[byte];
*codep = (*state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte);
*state = utf8d[256 + *state*16 + type];
return *state;
}
int is_utf8(uint8_t *s) {
uint32_t codepoint, state = 0;
while (*s)
decode(&state, &codepoint, *s++);
return state == UTF8_ACCEPT;
}
int is_utf8_len(uint8_t *s, size_t len) {
uint32_t codepoint, state = 0;
size_t i;
for (i = 0; i < len; i++)
decode(&state, &codepoint, *s++);
return state == UTF8_ACCEPT;
}
uint32_t is_utf8_len_state(uint8_t *s, size_t len, uint32_t state) {
uint32_t codepoint;
size_t i;
for (i = 0; i < len; i++)
decode(&state, &codepoint, *s++);
return state;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +0,0 @@
# Copyright (C) 2016-2021 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
if(ENABLE_WSSERVER_TEST)
find_program(SHELL sh)
if(SHELL)
add_test(NAME Autobahn|Testsuite
COMMAND "${SHELL}" "${CMAKE_CURRENT_SOURCE_DIR}/run-autobahn.sh" "CMAKE"
)
else()
message(FATAL_ERROR "Unable to locate shell")
endif(SHELL)
endif(ENABLE_WSSERVER_TEST)

View File

@@ -1,37 +0,0 @@
# Copyright (C) 2016-2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
WSDIR = $(CURDIR)/../
EXAMPLE = $(WSDIR)/examples/echo/echo
JSON = $(CURDIR)/wsserver_autobahn/index.json
# Conflicts
.PHONY: all run_autobahn check_results clean
# Examples
all: run_autobahn
$(MAKE) check_results
# Run autobahn
run_autobahn:
@bash run-autobahn.sh
# Check results
check_results:
@python validate_output.py
# Clean
clean:
@rm -rf wsserver_autobahn/report

View File

@@ -1,38 +0,0 @@
# Copyright (C) 2016-2020 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
CC = afl-gcc
WSDIR = $(CURDIR)/../../
INCLUDE = -I $(WSDIR)/include
CFLAGS += -Wall -Wextra -g -DVERBOSE_MODE -DAFL_FUZZ
CFLAGS += $(INCLUDE) -std=c99 -pthread -pedantic
LIB = $(WSDIR)/libws.a
.PHONY: all run_fuzzy clean
# Examples
all: ws_file run_fuzzy
# ws_file
ws_file: ws_file.c $(LIB)
$(CC) $(CFLAGS) $(LDFLAGS) ws_file.c -o ws_file $(LIB)
# Run fuzzing tests
run_fuzzy: ws_file
@bash run-fuzzy.sh
# Clean
clean:
@rm -f ws_file

View File

@@ -1,15 +0,0 @@
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Upgrade: websocket
Origin: file://
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en;q=0.8,es;q=0.7,ja;q=0.6
Sec-WebSocket-Key: 6d9YPRqkE/4/sU4/POk7ww==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
<EFBFBD><EFBFBD>Ì<>4f<34><66>Ì<>4f<34>þüI(}(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Iêˆûñ<C3BB>§ø

View File

@@ -1,15 +0,0 @@
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Upgrade: websocket
Origin: file://
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en;q=0.8,es;q=0.7,ja;q=0.6
Sec-WebSocket-Key: 6d9YPRqkE/4/sU4/POk7ww==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
<EFBFBD><EFBFBD>Ì<>4fˆûñ<C3BB>§ø

View File

@@ -1,15 +0,0 @@
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Upgrade: websocket
Origin: file://
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en;q=0.8,es;q=0.7,ja;q=0.6
Sec-WebSocket-Key: 6d9YPRqkE/4/sU4/POk7ww==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
<EFBFBD>þüI(}(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Ió(Iêˆûñ<C3BB>§ø

View File

@@ -1,15 +0,0 @@
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Upgrade: websocket
Origin: file://
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en;q=0.8,es;q=0.7,ja;q=0.6
Sec-WebSocket-Key: 6d9YPRqkE/4/sU4/POk7ww==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
ˆûñ<EFBFBD>§ø

View File

@@ -1,16 +0,0 @@
GET / HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: null
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: j8n3iFH1vFfiTp1xmWAIuw==
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
<EFBFBD><EFBFBD>Ì<>4fˆûñ<C3BB>§ø

View File

@@ -1,16 +0,0 @@
GET / HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: null
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: j8n3iFH1vFfiTp1xmWAIuw==
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
‰€Z¯a\‰€Z¯a\ˆûñ<C3BB>§ø

View File

@@ -1 +0,0 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

Some files were not shown because too many files have changed in this diff Show More