mirror of
https://github.com/Wessel/c-websocket-server.git
synced 2026-07-08 21:25:02 +02:00
chore: clean up repo + readme
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,9 +1,8 @@
|
||||
!.gitkeep
|
||||
|
||||
tmp/
|
||||
src/tmp
|
||||
|
||||
node_modules/
|
||||
obj/**/*
|
||||
out/**/*
|
||||
|
||||
obj/
|
||||
out/
|
||||
!.gitkeep
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "git/wsserver"]
|
||||
path = git/wsserver
|
||||
url = https://github.com/theldus/wsserver
|
||||
79
README.md
79
README.md
@@ -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:
|
||||
|
||||
[](/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> }, ... ]
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
...
|
||||
27
git/wsserver/.gitignore
vendored
27
git/wsserver/.gitignore
vendored
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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>.
|
||||
@@ -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
|
||||
@@ -1,188 +0,0 @@
|
||||
|
||||
# wsServer
|
||||
[](https://opensource.org/licenses/GPL-3.0)
|
||||
[](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.
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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
@@ -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 %}
|
||||
@@ -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<{% for t in compound.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}></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 %}
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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">…</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 …" 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 %}
|
||||
@@ -1 +0,0 @@
|
||||
{% extends 'base-class-reference.html' %}
|
||||
@@ -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>
|
||||
@@ -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<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}>
|
||||
</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>
|
||||
@@ -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<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}>
|
||||
{% endif %}
|
||||
{% if func.templates != None %}
|
||||
{% set j = joiner(', ') %}
|
||||
template<{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}>
|
||||
{% 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' %} <span class="m-label m-flat m-info">in</span>{% elif param.direction == 'out' %} <span class="m-label m-flat m-warning">out</span>{% elif param.direction == 'inout' %} <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>
|
||||
@@ -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<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}>
|
||||
{% endif %}
|
||||
{% if typedef.templates != None %}
|
||||
{% set j = joiner(', ') %}
|
||||
template<{% for t in typedef.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}>
|
||||
{% 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>
|
||||
@@ -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<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}>
|
||||
{% endif %}
|
||||
{% if var.templates != None %}
|
||||
{% set j = joiner(', ') %}
|
||||
template<{% for t in var.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}>
|
||||
{% 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>
|
||||
@@ -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 %}
|
||||
@@ -1,10 +0,0 @@
|
||||
<dt>
|
||||
{% if class.templates != None %}
|
||||
{% set j = joiner(', ') %}
|
||||
<div class="m-doc-template">template<{% for t in class.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}></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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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<{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}></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' %} -> {{ 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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<{% for t in typedef.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}></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>
|
||||
@@ -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<{% for t in var.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}></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>
|
||||
@@ -1,30 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{% set j = joiner(' » ') %}{% 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> »</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">« {{ 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] }} »</a>{% endif %}</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -1,11 +0,0 @@
|
||||
{% extends 'base-reference.html' %}
|
||||
|
||||
{% block title %}{% set j = joiner(' » ') %}{% 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> »</span>
|
||||
{% endfor %}
|
||||
{{ compound.breadcrumb[-1][0] }} <span class="m-thin">module</span>{% if compound.since %} {{ compound.since }}{% endif %}</h1>
|
||||
{% endblock %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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>
|
||||
@@ -1,51 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{% if 1 in compound.breadcrumb or compound.breadcrumb[-1][0] != PROJECT_NAME %}{% set j = joiner(' » ') %}{% 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> »</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">« {{ 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] }} »</a>{% endif %}</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -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 %}
|
||||
@@ -1 +0,0 @@
|
||||
{% extends 'base-class-reference.html' %}
|
||||
@@ -1 +0,0 @@
|
||||
{% extends 'base-class-reference.html' %}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 ()
|
||||
@@ -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
|
||||
```
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
```
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>§ø
|
||||
@@ -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>§ø
|
||||
@@ -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>§ø
|
||||
@@ -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>§ø
|
||||
@@ -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>§ø
|
||||
Binary file not shown.
@@ -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>§ø
|
||||
@@ -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
Reference in New Issue
Block a user