Compare commits
188 Commits
cafile
...
fix-compil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfb3e86824 | ||
|
|
a81f0a8a5f | ||
|
|
5509ec3b35 | ||
|
|
67b1a2f3f4 | ||
|
|
7c2e78937d | ||
|
|
b7bae1502f | ||
|
|
e3d734c5ff | ||
|
|
e7b08faaa7 | ||
|
|
f3fa9f557b | ||
|
|
5690c4ed89 | ||
|
|
62953e9ecf | ||
|
|
5142eea2eb | ||
|
|
5dd9e4b0e0 | ||
|
|
4ec4f09b35 | ||
|
|
e24819a405 | ||
|
|
d0d1fa9835 | ||
|
|
da1229814c | ||
|
|
d82df1273c | ||
|
|
9fef4b7d02 | ||
|
|
4359536a17 | ||
|
|
03d43132bc | ||
|
|
4d59a9fe45 | ||
|
|
40f2452fb0 | ||
|
|
1cf09b1870 | ||
|
|
5edc480932 | ||
|
|
641211ecae | ||
|
|
d8fd6e6d14 | ||
|
|
9410530507 | ||
|
|
d6d71e97f8 | ||
|
|
3f03de031c | ||
|
|
c7d410f38b | ||
|
|
fa960429b8 | ||
|
|
bfd0872aea | ||
|
|
c5b6026fc6 | ||
|
|
7fbf507f58 | ||
|
|
69f42360cf | ||
|
|
f40fcbe5d1 | ||
|
|
13d0354176 | ||
|
|
53d8eb17b6 | ||
|
|
9735af515a | ||
|
|
c30bd0d9ab | ||
|
|
fdd717a246 | ||
|
|
7894d15929 | ||
|
|
f49ccb3262 | ||
|
|
069787db51 | ||
|
|
9eba8e8738 | ||
|
|
f13ea6e556 | ||
|
|
29f68c0dd7 | ||
|
|
3c0966d74e | ||
|
|
c90128779f | ||
|
|
acb8e0f629 | ||
|
|
f3460460e9 | ||
|
|
37555868f6 | ||
|
|
b0855f75f5 | ||
|
|
977ef75b14 | ||
|
|
d7d7435f21 | ||
|
|
d22558e0e3 | ||
|
|
96ece6fbdb | ||
|
|
a60d2ea19d | ||
|
|
70a908e18b | ||
|
|
004a5bf7fc | ||
|
|
e9bd06354a | ||
|
|
54b92c0255 | ||
|
|
6881a2e770 | ||
|
|
e29f422511 | ||
|
|
1a8be1d617 | ||
|
|
8ea15cf656 | ||
|
|
a8807feb5c | ||
|
|
4d15f1db0b | ||
|
|
957725348e | ||
|
|
357edbd367 | ||
|
|
3d01915407 | ||
|
|
e90a6e9f71 | ||
|
|
3891b73c44 | ||
|
|
a66c3234d7 | ||
|
|
2a81327ab0 | ||
|
|
1d6714c59b | ||
|
|
a771b3d4fe | ||
|
|
700abd95b6 | ||
|
|
476dd4c4f7 | ||
|
|
f0383d50cd | ||
|
|
044398eeba | ||
|
|
675ba5b3fc | ||
|
|
62d1063560 | ||
|
|
d8c9ca49e1 | ||
|
|
91d6dac985 | ||
|
|
d7bd434f41 | ||
|
|
5ac415986e | ||
|
|
266fd0b1d4 | ||
|
|
10760238b3 | ||
|
|
d9acad3d91 | ||
|
|
27278c032b | ||
|
|
57bc4bbb75 | ||
|
|
3600756723 | ||
|
|
7111ee2f85 | ||
|
|
dcee2eea91 | ||
|
|
4a0c71a840 | ||
|
|
e1ee758b61 | ||
|
|
4b5e103d9c | ||
|
|
bddb80a192 | ||
|
|
06afe3d1c2 | ||
|
|
97b1452211 | ||
|
|
fc6ba89c61 | ||
|
|
a92115ab9f | ||
|
|
08158ee8ad | ||
|
|
528c16e6c8 | ||
|
|
870f2174d5 | ||
|
|
1406198087 | ||
|
|
53373261ad | ||
|
|
c1c697b177 | ||
|
|
808a981e86 | ||
|
|
879f40a0ae | ||
|
|
0365f03342 | ||
|
|
1e8dc3caa8 | ||
|
|
e32d39dbc1 | ||
|
|
2bc34b5847 | ||
|
|
4cf7805e9e | ||
|
|
a1206435eb | ||
|
|
69550f9be9 | ||
|
|
9c7b8580d8 | ||
|
|
ec5e942705 | ||
|
|
404464fd40 | ||
|
|
619bcc38e9 | ||
|
|
728e050a05 | ||
|
|
93e04b8d03 | ||
|
|
a97714da18 | ||
|
|
118087f2a1 | ||
|
|
3a3b4faa10 | ||
|
|
2850fd7792 | ||
|
|
db91f793dc | ||
|
|
3225bafd54 | ||
|
|
47f4d883c8 | ||
|
|
d8d0e75466 | ||
|
|
e9d983dec2 | ||
|
|
f3878b954a | ||
|
|
f693efd532 | ||
|
|
6a052ed0fc | ||
|
|
edb54ebe90 | ||
|
|
28f3ce19b8 | ||
|
|
b0631e322f | ||
|
|
c3c28cb4c1 | ||
|
|
ae5484ebd5 | ||
|
|
01f2c3128a | ||
|
|
07a4f778d3 | ||
|
|
44a5556278 | ||
|
|
b3e355736f | ||
|
|
058576e768 | ||
|
|
1c84ca3730 | ||
|
|
bf1348e89d | ||
|
|
795675d1aa | ||
|
|
12009a009d | ||
|
|
f23ac83c95 | ||
|
|
2dc20d13fb | ||
|
|
e26548375e | ||
|
|
e73cdb57f3 | ||
|
|
fcc4f8c680 | ||
|
|
a1de389e45 | ||
|
|
4ef5b2d449 | ||
|
|
ad9e9d5dbd | ||
|
|
4e53184eee | ||
|
|
f19ed572f0 | ||
|
|
f27d95fe43 | ||
|
|
2bf1988b3c | ||
|
|
05620ab506 | ||
|
|
0ea9c1d3a2 | ||
|
|
1f0a3f1ad1 | ||
|
|
2f491e0250 | ||
|
|
2509ed8424 | ||
|
|
f3e3e64a17 | ||
|
|
d7a28f97a6 | ||
|
|
2199d8d088 | ||
|
|
d1ce4b35ae | ||
|
|
e77cd53215 | ||
|
|
588aea4f54 | ||
|
|
ee5f9fb77b | ||
|
|
4790a61437 | ||
|
|
25d3a2d459 | ||
|
|
27be6acaeb | ||
|
|
9a0e070c24 | ||
|
|
e490011b68 | ||
|
|
3b4b664c28 | ||
|
|
18c95fa7bd | ||
|
|
cf2f7147d9 | ||
|
|
ecf6457cdb | ||
|
|
db8a511f68 | ||
|
|
2d5424bcff | ||
|
|
5792d52461 | ||
|
|
09229e2cd5 |
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
||||
# Unify coding style with clang-format
|
||||
562a06425b96450481cdbc88e8872a2bf5a7d8aa
|
||||
49
.github/ISSUE_TEMPLATE.md
vendored
Normal file
49
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<!--
|
||||
|
||||
Provide a general summary of the issue in the Title above
|
||||
|
||||
This is a very generic template, remove items that do not apply. For completed items, change [ ] to [x].
|
||||
|
||||
-->
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* [ ] Checked that your issue isn't already filed: https://github.com/strophe/libstrophe/issues?q=
|
||||
|
||||
### Additional Information
|
||||
|
||||
<!--- Any additional information, configuration or data that might be necessary to reproduce the issue. -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behavior
|
||||
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior or even better, provide a functional code example reproducing your issue. -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
<!--- or ideas how to implement the addition or change -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
|
||||
<!--- Please either describe your issue in detail or even better, provide a functional code example reproducing your issue. -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context
|
||||
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
|
||||
## Environment
|
||||
|
||||
<!---
|
||||
Please provide the version of the library you're using.
|
||||
You can find the version either in the first line of the `configure.ac` file or if you're using the Git repository, please run `git describe --always --tags --dirty`.
|
||||
Also, please include the compiler, the compiler version, the architecture, the OS and what version of the OS you're experiencing the issue.
|
||||
-->
|
||||
161
.github/workflows/main.yml
vendored
Normal file
161
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
linux-tests:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
valgrind:
|
||||
- { configure: '' , cflags: '', make: 'check' }
|
||||
- { configure: '--enable-valgrind' , cflags: '-O2', make: 'check-valgrind' }
|
||||
options:
|
||||
- { configure: '' }
|
||||
- { configure: '--without-libxml2' }
|
||||
- { configure: '--with-libxml2' }
|
||||
- { configure: '--with-gnutls' }
|
||||
- { configure: '--disable-tls' }
|
||||
- { configure: '--enable-cares' }
|
||||
- { configure: '--disable-getrandom' }
|
||||
- { configure: '--disable-static' }
|
||||
name: Regular Tests
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libgnutls28-dev libc-ares-dev valgrind
|
||||
- name: Build the library
|
||||
run: |
|
||||
./bootstrap.sh
|
||||
./configure ${{ matrix.options.configure }} ${{ matrix.valgrind.configure }} CFLAGS="-Werror -g3 ${{ matrix.valgrind.cflags }}"
|
||||
make -j$(nproc)
|
||||
- name: Run tests
|
||||
run: |
|
||||
make -j$(nproc) ${{ matrix.valgrind.make }}
|
||||
- name: Error logs
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
cat test-suite*.log || true
|
||||
|
||||
xssl-tests:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
xssl_versions:
|
||||
- { version: "master", continue: true, libressl: true }
|
||||
- { version: "OPENBSD_7_5", continue: true, libressl: true }
|
||||
- { version: "v3.9.2", continue: true, libressl: true }
|
||||
- { version: "OPENBSD_7_4", continue: true, libressl: true }
|
||||
- { version: "v3.8.4", continue: true, libressl: true }
|
||||
- { version: "OPENBSD_7_3", continue: true, libressl: true }
|
||||
- { version: "OPENBSD_7_2", continue: true, libressl: true }
|
||||
- { version: "OPENBSD_7_1", continue: true, libressl: true }
|
||||
# https://github.com/libressl-portable/portable/issues/760
|
||||
# - { version: "v3.5.2", continue: true, libressl: true }
|
||||
- { version: "OPENBSD_7_0", continue: true, libressl: true }
|
||||
# OPENBSD_7_0 is basically the "fixed v3.4.3"
|
||||
# - { version: "v3.4.3", continue: true, libressl: true }
|
||||
- { version: "v3.4.2", continue: true, libressl: true }
|
||||
- { version: "OPENBSD_6_9", continue: true, libressl: true }
|
||||
- { version: "v3.1.5", continue: true, libressl: true }
|
||||
- { version: "v2.1.10", continue: true, libressl: true }
|
||||
- { version: "openssl-3.0", continue: true, libressl: false }
|
||||
- { version: "openssl-3.0.13", continue: false, libressl: false }
|
||||
- { version: "openssl-3.1", continue: true, libressl: false }
|
||||
- { version: "openssl-3.1.5", continue: false, libressl: false }
|
||||
- { version: "openssl-3.2", continue: true, libressl: false }
|
||||
- { version: "openssl-3.2.1", continue: false, libressl: false }
|
||||
- { version: "openssl-3.3", continue: true, libressl: false }
|
||||
- { version: "openssl-3.3.0", continue: false, libressl: false }
|
||||
name: xSSL tests
|
||||
continue-on-error: ${{ matrix.xssl_versions.continue }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y libtool pkg-config libexpat1-dev valgrind
|
||||
- name: build&install the TLS stack
|
||||
env:
|
||||
XSSL_COMMITISH: ${{ matrix.xssl_versions.version }}
|
||||
LIBRESSL: ${{ matrix.xssl_versions.libressl }}
|
||||
run: |
|
||||
./travis/before_script.sh
|
||||
- name: Build the library
|
||||
run: |
|
||||
./bootstrap.sh
|
||||
PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" ./configure CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
|
||||
make -j$(nproc)
|
||||
- name: Run tests
|
||||
run: |
|
||||
LD_LIBRARY_PATH="${HOME}/xssl/lib" make -j$(nproc) check
|
||||
- name: Build the library with Valgrind enabled
|
||||
run: |
|
||||
./bootstrap.sh
|
||||
PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" ./configure --enable-valgrind CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
|
||||
make -j$(nproc)
|
||||
- name: Run tests with Valgrind enabled
|
||||
run: |
|
||||
LD_LIBRARY_PATH="${HOME}/xssl/lib" make -j$(nproc) check-valgrind
|
||||
- name: Error logs
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
cat test-suite*.log || true
|
||||
|
||||
release-test:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Check if release would work
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install dependencies & bootstrap
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y libtool pkg-config libexpat1-dev dash
|
||||
./bootstrap.sh
|
||||
- name: Check if configure works with non-bash shells
|
||||
# https://github.com/actions/runner/issues/241 requires us to use this following line...
|
||||
shell: 'script --return --quiet --command "bash {0}"'
|
||||
run: |
|
||||
[ "`CONFIG_SHELL=/bin/dash ./configure 2>&1 1>/dev/null | tee /dev/tty | wc -l`" = "0" ]
|
||||
- name: Re-run configure with the default shell
|
||||
run: |
|
||||
./configure
|
||||
- name: Try release & tests
|
||||
run: |
|
||||
make test-release
|
||||
- name: Show logs from release build
|
||||
if: ${{ !failure() }}
|
||||
run: |
|
||||
cat testbuild.log
|
||||
- name: Error logs
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
cat testbuild.log || true
|
||||
cat testerr.log || true
|
||||
|
||||
code-style:
|
||||
runs-on: ubuntu-24.04
|
||||
name: Check coding style
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libgnutls28-dev libc-ares-dev dos2unix
|
||||
- name: Configure
|
||||
run: |
|
||||
./bootstrap.sh
|
||||
./configure
|
||||
- name: Check style
|
||||
run: |
|
||||
make format
|
||||
git diff --exit-code
|
||||
23
.gitignore
vendored
23
.gitignore
vendored
@@ -20,6 +20,8 @@ autom4te.cache
|
||||
.dirstamp
|
||||
.deps
|
||||
.sconsign*
|
||||
src/*.gcda
|
||||
src/*.gcno
|
||||
src/*.o
|
||||
src/*.lo
|
||||
examples/*.o
|
||||
@@ -27,6 +29,8 @@ examples/*.o
|
||||
*.la
|
||||
*.pc
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
*.tar.xz
|
||||
*.zip
|
||||
docs/html
|
||||
TAGS
|
||||
@@ -34,19 +38,26 @@ tags
|
||||
examples/active
|
||||
examples/basic
|
||||
examples/bot
|
||||
examples/complex
|
||||
examples/component
|
||||
examples/perf
|
||||
examples/register
|
||||
examples/roster
|
||||
examples/uuid
|
||||
examples/vcard
|
||||
testbuild*.log
|
||||
testerr*.log
|
||||
test-release/
|
||||
test_stamp
|
||||
test-suite.log
|
||||
test-suite*.log
|
||||
tests/*.o
|
||||
tests/*.log
|
||||
tests/*.trs
|
||||
tests/check_parser
|
||||
tests/test_base64
|
||||
tests/test_ctx
|
||||
tests/test_fuzz_parser
|
||||
tests/test_fuzz_resolver
|
||||
tests/test_hash
|
||||
tests/test_jid
|
||||
tests/test_md5
|
||||
@@ -54,6 +65,8 @@ tests/test_rand
|
||||
tests/test_resolver
|
||||
tests/test_sasl
|
||||
tests/test_scram
|
||||
tests/test_send_queue
|
||||
tests/test_serialize_sm
|
||||
tests/test_sha1
|
||||
tests/test_sha256
|
||||
tests/test_sha512
|
||||
@@ -61,7 +74,9 @@ tests/test_snprintf
|
||||
tests/test_sock
|
||||
tests/test_stanza
|
||||
tests/test_string
|
||||
m4/
|
||||
tests/test_xmppaddr
|
||||
m4/lt*
|
||||
m4/libtool*
|
||||
libstrophe.project
|
||||
libs/
|
||||
obj/
|
||||
@@ -69,3 +84,7 @@ expat/
|
||||
.settings/
|
||||
.project
|
||||
.cproject
|
||||
coverage/
|
||||
coverage.info
|
||||
configure~
|
||||
fuzz-*.log
|
||||
|
||||
26
.travis.yml
26
.travis.yml
@@ -1,26 +0,0 @@
|
||||
language: c
|
||||
install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y install libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libc-ares-dev dos2unix
|
||||
stages:
|
||||
- style
|
||||
- test
|
||||
before_script:
|
||||
- ./travis/before_script.sh
|
||||
env:
|
||||
- CONFIGURE_OPT="--without-libxml2"
|
||||
- CONFIGURE_OPT="--with-libxml2"
|
||||
- CONFIGURE_OPT="--disable-tls"
|
||||
- CONFIGURE_OPT="--enable-cares"
|
||||
- CONFIGURE_OPT="PKG_CONFIG_PATH=${HOME}/libressl/lib/pkgconfig" LIBRESSL=yes LIBRESSL_COMMIT="v3.1.4"
|
||||
- CONFIGURE_OPT="PKG_CONFIG_PATH=${HOME}/libressl/lib/pkgconfig" LIBRESSL=yes LIBRESSL_COMMIT="v2.1.7"
|
||||
script: ./bootstrap.sh && ./configure ${CONFIGURE_OPT} CFLAGS="-Werror" && make && make check
|
||||
jobs:
|
||||
include:
|
||||
- stage: style
|
||||
name: "Check code style"
|
||||
script: ./bootstrap.sh && ./configure && make format && git diff --exit-code
|
||||
env:
|
||||
- CONFIGURE_OPT=""
|
||||
matrix:
|
||||
fast_finish: true
|
||||
7
AUTHORS
7
AUTHORS
@@ -1 +1,8 @@
|
||||
Current maintainer:
|
||||
Steffen Jaeckel <gh@jaeckel.eu>
|
||||
|
||||
Previous maintainers:
|
||||
Dmitry Podgorny <pasis.ua@gmail.com>
|
||||
Jack Moffit <jack@metajack.im>
|
||||
|
||||
Other contributors can be extracted from the Git log.
|
||||
|
||||
124
ChangeLog
124
ChangeLog
@@ -1,5 +1,129 @@
|
||||
0.14.0
|
||||
- Add SM-state serialization mechanism (#239)
|
||||
- Add support for `SCRAM-SHA-512-PLUS` (da122981)
|
||||
- Add support for `SSLKEYLOGFILE` with OpenSSL (d82df127)
|
||||
- Fix some issues related to Stream Management (03d43132, 4359536a)
|
||||
- Fix a potential segfault (9fef4b7d)
|
||||
- New API:
|
||||
- xmpp_conn_set_sm_callback()
|
||||
- xmpp_conn_restore_sm_state()
|
||||
|
||||
0.13.1
|
||||
- Fix SCRAM-*-PLUS SASL mechanisms with OpenSSL and TLS < v1.3 (40f2452)
|
||||
- Only signal "stream negotiation success" once (1cf09b1)
|
||||
- Fix `sock_connect()` not looping over all DNS records returned if no `sockopt_cb` is set (5edc480)
|
||||
- Replace usage of EBADFD, it's not in POSIX (#235)
|
||||
|
||||
0.13.0
|
||||
- Fix connected/connecting signaling to user (#227)
|
||||
- Fix wording of licensing terms (#225)
|
||||
- Prepare for future changes in OpenSSL (#226)
|
||||
- Improve Stream Management (#227) (#230)
|
||||
- Add SCRAM-PLUS Variants (#228)
|
||||
- Introduce XEP-0138 stream compression (#231)
|
||||
- Deprecated the following API (#227):
|
||||
- xmpp_conn_disable_tls() - replaced by a flag set by xmpp_conn_set_flags()
|
||||
|
||||
0.12.3
|
||||
- Improve TCP-connection establishment (#221)
|
||||
- Handle case where the server doesn't provide the `bind` feature (#224)
|
||||
- Fix configure script for non-bash shells (#218)
|
||||
- Parse JID's according to RFC7622 (#219)
|
||||
- Fix potential memory leak in internal DNS resolver (#219)
|
||||
- Fix potential memory leaks in `xmpp_conn_set_cafile()` and `xmpp_conn_set_capath()` (#219)
|
||||
- Internal improvements (#219)
|
||||
|
||||
0.12.2
|
||||
- Fix reconnect issues when Stream Management is enabled (#211)
|
||||
- Fix resolver ... this time for real hopefully (fixup of #200) (#214)
|
||||
- Fix clearing of password cache on resumed connection (#214)
|
||||
- Improve detection&implementation of `va_copy` (#213)
|
||||
- Fix Valgrind CI builds against LibreSSL (#212)
|
||||
- Fix perf example on platforms where an `rdtsc()` equivalent isn't implemented (#212)
|
||||
|
||||
0.12.1
|
||||
- Fix compilation in buildroot (#207)
|
||||
- Fixes regarding OpenSSL (#208)
|
||||
- Fix some build steps when builddir != srcdir (#208)
|
||||
- Allow the user to disable build of examples (#209)
|
||||
- CI builds against OpenSSL 3 (#206)
|
||||
- Change the call signature of the following API (#208):
|
||||
- xmpp_conn_set_client_cert() - the PKCS#12 file has now to be passed via the `cert`
|
||||
parameter. Originally it was via `key`. Currently both styles are supported,
|
||||
but in a future release only passing via `cert` will be accepted.
|
||||
|
||||
0.12.0
|
||||
- Fix potential infinite loop in resolver (#200)
|
||||
- Prevent potential memory leak in `xmpp_stanza_new_from_string()` (#205)
|
||||
- Do proper SO versioning
|
||||
- Add code coverage support (#188)
|
||||
- Add support for password-protected TLS key & PKCS#12/PFX files (#195, #205)
|
||||
- Stream-Management support - XEP-0198 (#184)
|
||||
- New API:
|
||||
- xmpp_conn_send_queue_len()
|
||||
- xmpp_conn_send_queue_drop_element()
|
||||
- xmpp_conn_get_sm_state()
|
||||
- xmpp_conn_set_sm_state()
|
||||
- xmpp_free_sm_state()
|
||||
- xmpp_conn_get_keyfile()
|
||||
- xmpp_conn_set_password_callback()
|
||||
- xmpp_conn_set_password_retries()
|
||||
- xmpp_stanza_get_child_by_path()
|
||||
- xmpp_conn_set_sockopt_callback()
|
||||
- xmpp_sockopt_cb_keepalive()
|
||||
- The following APIs were public in the shared library, but not in `strophe.h`.
|
||||
Now they are officially public API:
|
||||
- xmpp_rand_new()
|
||||
- xmpp_rand_free()
|
||||
- xmpp_rand()
|
||||
- xmpp_rand_bytes()
|
||||
- xmpp_rand_nonce()
|
||||
- xmpp_rand_bytes()
|
||||
- The following APIs were public in the shared library, but not in `strophe.h`.
|
||||
In a future version of the library they will be private without replacement:
|
||||
- xmpp_alloc()
|
||||
- xmpp_realloc()
|
||||
- xmpp_strdup()
|
||||
- xmpp_strndup()
|
||||
- xmpp_strtok_r()
|
||||
- xmpp_snprintf()
|
||||
- xmpp_vsnprintf()
|
||||
- xmpp_log()
|
||||
- xmpp_error()
|
||||
- xmpp_warn()
|
||||
- xmpp_info()
|
||||
- xmpp_debug()
|
||||
- xmpp_debug_verbose()
|
||||
- Deprecated the following API:
|
||||
- xmpp_conn_set_keepalive() - replaced by xmpp_conn_set_sockopt_callback()
|
||||
|
||||
0.11.0
|
||||
- SASL EXTERNAL support (XEP-0178)
|
||||
- Client certificate can be provided for TLS negotiation. If the
|
||||
certificate contains a single xmppAddr and JID is not provided with
|
||||
xmpp_conn_set_jid(), the xmppAddr is chosen as JID
|
||||
- <stream> element contains "from" attribute over TLS connections now
|
||||
- GnuTLS can be selected optionally with configure script
|
||||
- Support for manual certificate verification
|
||||
- New API:
|
||||
- xmpp_conn_set_client_cert()
|
||||
- xmpp_conn_cert_xmppaddr_num()
|
||||
- xmpp_conn_cert_xmppaddr()
|
||||
- xmpp_conn_set_cafile()
|
||||
- xmpp_conn_set_capath()
|
||||
- xmpp_conn_set_certfail_handler()
|
||||
- xmpp_conn_get_peer_cert()
|
||||
- xmpp_tlscert_get_ctx()
|
||||
- xmpp_tlscert_get_conn()
|
||||
- xmpp_tlscert_get_pem()
|
||||
- xmpp_tlscert_get_dnsname()
|
||||
- xmpp_tlscert_get_string()
|
||||
- xmpp_tlscert_get_description()
|
||||
- xmpp_tlscert_free()
|
||||
|
||||
0.10.1
|
||||
- Fixed compilation error when LibreSSL is used
|
||||
- Fixed crash when NULL is provided as password
|
||||
|
||||
0.10.0
|
||||
- Coding style has been unified
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
libstrophe XMPP client library
|
||||
Copyright (C) 2005-2009 Collecta, Inc.
|
||||
|
||||
This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
Please the files MIT-LICENSE.txt and GPL-LICENSE.txt for details.
|
||||
|
||||
182
Makefile.am
182
Makefile.am
@@ -2,32 +2,50 @@ AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
COVERAGE_CFLAGS=@COVERAGE_CFLAGS@
|
||||
COVERAGE_LDFLAGS=@COVERAGE_LDFLAGS@
|
||||
COVERAGE_PRE=@COVERAGE_PRE@
|
||||
COVERAGE_POST=@COVERAGE_POST@
|
||||
|
||||
PARSER_CFLAGS=@PARSER_CFLAGS@
|
||||
PARSER_LIBS=@PARSER_LIBS@
|
||||
|
||||
ZLIB_CFLAGS=@ZLIB_CFLAGS@
|
||||
ZLIB_LIBS=@ZLIB_LIBS@
|
||||
|
||||
if TLS_WITH_GNUTLS
|
||||
SSL_CFLAGS = @gnutls_CFLAGS@
|
||||
SSL_LIBS = @gnutls_LIBS@
|
||||
else
|
||||
if !TLS_WITH_SCHANNEL
|
||||
SSL_CFLAGS = @openssl_CFLAGS@
|
||||
SSL_LIBS = @openssl_LIBS@
|
||||
endif
|
||||
endif
|
||||
|
||||
MINGW_LIBS = @MINGW_LIBS@
|
||||
|
||||
RESOLV_CFLAGS = @RESOLV_CFLAGS@
|
||||
RESOLV_LIBS = @RESOLV_LIBS@
|
||||
|
||||
WARNING_FLAGS = @WARNING_FLAGS@
|
||||
STROPHE_FLAGS = -I$(top_srcdir) $(WARNING_FLAGS)
|
||||
STROPHE_LIBS = libstrophe.la
|
||||
STROPHE_LIBS = $(COVERAGE_PRE) libstrophe.la $(COVERAGE_POST) $(COVERAGE_LDFLAGS)
|
||||
|
||||
## Main build targets
|
||||
lib_LTLIBRARIES = libstrophe.la
|
||||
|
||||
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS)
|
||||
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) -no-undefined
|
||||
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS) $(COVERAGE_CFLAGS) $(ZLIB_CFLAGS)
|
||||
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) $(MINGW_LIBS) $(ZLIB_LIBS) -no-undefined
|
||||
# Export only public API
|
||||
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_'
|
||||
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' -version-info @VERSION_INFO@
|
||||
|
||||
libstrophe_la_SOURCES = \
|
||||
src/auth.c \
|
||||
src/conn.c \
|
||||
src/crypto.c \
|
||||
src/ctx.c \
|
||||
src/deprecated.c \
|
||||
src/event.c \
|
||||
src/handler.c \
|
||||
src/hash.c \
|
||||
@@ -40,9 +58,9 @@ libstrophe_la_SOURCES = \
|
||||
src/sha1.c \
|
||||
src/sha256.c \
|
||||
src/sha512.c \
|
||||
src/snprintf.c \
|
||||
src/sock.c \
|
||||
src/stanza.c \
|
||||
src/tls.c \
|
||||
src/util.c \
|
||||
src/uuid.c
|
||||
libstrophe_la_SOURCES += \
|
||||
@@ -51,7 +69,6 @@ libstrophe_la_SOURCES += \
|
||||
src/md5.h \
|
||||
src/ostypes.h \
|
||||
src/parser.h \
|
||||
src/rand.h \
|
||||
src/resolver.h \
|
||||
src/sasl.h \
|
||||
src/scram.h \
|
||||
@@ -64,11 +81,29 @@ libstrophe_la_SOURCES += \
|
||||
src/tls.h \
|
||||
src/util.h
|
||||
|
||||
if NEED_SNPRINTF
|
||||
libstrophe_la_SOURCES += src/snprintf.c
|
||||
endif
|
||||
|
||||
if DISABLE_COMPRESSION
|
||||
libstrophe_la_SOURCES += src/compression_dummy.c
|
||||
else
|
||||
libstrophe_la_SOURCES += src/compression.c
|
||||
endif
|
||||
|
||||
if DISABLE_TLS
|
||||
libstrophe_la_SOURCES += src/tls_dummy.c
|
||||
else
|
||||
if TLS_WITH_GNUTLS
|
||||
libstrophe_la_SOURCES += src/tls_gnutls.c
|
||||
else
|
||||
if TLS_WITH_SCHANNEL
|
||||
libstrophe_la_SOURCES += src/tls_schannel.c
|
||||
else
|
||||
libstrophe_la_SOURCES += src/tls_openssl.c
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if PARSER_EXPAT
|
||||
libstrophe_la_SOURCES += src/parser_expat.c
|
||||
@@ -88,24 +123,32 @@ EXTRA_DIST = \
|
||||
MIT-LICENSE.txt \
|
||||
bootstrap.sh \
|
||||
build-android.sh \
|
||||
docs/footer.html \
|
||||
testbuild.sh \
|
||||
examples/README.md \
|
||||
jni/Android.mk \
|
||||
jni/Application.mk \
|
||||
src/tls_gnutls.c \
|
||||
src/tls_schannel.c \
|
||||
m4/ax_valgrind_check.m4 \
|
||||
tests/cert.pem \
|
||||
tests/cert.pfx \
|
||||
tests/cert.emptypass.pfx \
|
||||
tests/cert.nopass.pfx \
|
||||
tests/key.pem \
|
||||
tests/key_encrypted.pem \
|
||||
tests/res_query_dump.c
|
||||
|
||||
if EXAMPLES
|
||||
## Examples
|
||||
noinst_PROGRAMS = \
|
||||
examples/active \
|
||||
examples/basic \
|
||||
examples/bot \
|
||||
examples/complex \
|
||||
examples/component \
|
||||
examples/perf \
|
||||
examples/register \
|
||||
examples/roster \
|
||||
examples/uuid \
|
||||
examples/vcard \
|
||||
examples/register
|
||||
examples/vcard
|
||||
|
||||
examples_active_SOURCES = examples/active.c
|
||||
examples_active_CFLAGS = $(STROPHE_FLAGS)
|
||||
@@ -116,9 +159,18 @@ examples_basic_LDADD = $(STROPHE_LIBS)
|
||||
examples_bot_SOURCES = examples/bot.c
|
||||
examples_bot_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_bot_LDADD = $(STROPHE_LIBS)
|
||||
examples_complex_SOURCES = examples/complex.c
|
||||
examples_complex_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_complex_LDADD = $(STROPHE_LIBS)
|
||||
examples_component_SOURCES = examples/component.c
|
||||
examples_component_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_component_LDADD = $(STROPHE_LIBS)
|
||||
examples_perf_SOURCES = examples/perf.c
|
||||
examples_perf_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_perf_LDADD = $(STROPHE_LIBS)
|
||||
examples_register_SOURCES = examples/register.c
|
||||
examples_register_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_register_LDADD = $(STROPHE_LIBS)
|
||||
examples_roster_SOURCES = examples/roster.c
|
||||
examples_roster_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_roster_LDADD = $(STROPHE_LIBS)
|
||||
@@ -128,31 +180,57 @@ examples_uuid_LDADD = $(STROPHE_LIBS)
|
||||
examples_vcard_SOURCES = examples/vcard.c
|
||||
examples_vcard_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_vcard_LDADD = $(STROPHE_LIBS)
|
||||
examples_register_SOURCES = examples/register.c
|
||||
examples_register_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_register_LDADD = $(STROPHE_LIBS)
|
||||
endif
|
||||
|
||||
|
||||
## Tests
|
||||
TESTS = \
|
||||
STATIC_TESTS = \
|
||||
tests/check_parser \
|
||||
tests/test_hash \
|
||||
tests/test_jid \
|
||||
tests/test_ctx \
|
||||
tests/test_send_queue \
|
||||
tests/test_serialize_sm \
|
||||
tests/test_string \
|
||||
tests/test_resolver
|
||||
|
||||
TESTS = \
|
||||
tests/test_sha1 \
|
||||
tests/test_sha256 \
|
||||
tests/test_sha512 \
|
||||
tests/test_md5 \
|
||||
tests/test_rand \
|
||||
tests/test_scram \
|
||||
tests/test_ctx \
|
||||
tests/test_base64 \
|
||||
tests/test_hash \
|
||||
tests/test_jid \
|
||||
tests/test_snprintf \
|
||||
tests/test_string \
|
||||
tests/test_stanza \
|
||||
tests/test_resolver
|
||||
tests/test_stanza
|
||||
|
||||
if !DISABLE_STATIC
|
||||
TESTS += $(STATIC_TESTS)
|
||||
endif
|
||||
|
||||
if !DISABLE_TLS
|
||||
TESTS += tests/test_xmppaddr
|
||||
endif
|
||||
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
if FUZZ
|
||||
check_PROGRAMS += tests/test_fuzz_parser tests/test_fuzz_resolver
|
||||
|
||||
tests_test_fuzz_parser_SOURCES = tests/test_fuzz_parser.c
|
||||
tests_test_fuzz_parser_CFLAGS = -fsanitize=fuzzer,address $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
|
||||
-I$(top_srcdir)/src
|
||||
tests_test_fuzz_parser_LDADD = $(STROPHE_LIBS)
|
||||
tests_test_fuzz_parser_LDFLAGS = -static
|
||||
|
||||
tests_test_fuzz_resolver_SOURCES = tests/test_fuzz_resolver.c
|
||||
tests_test_fuzz_resolver_CFLAGS = -fsanitize=fuzzer,address $(resolver_CFLAGS) $(STROPHE_FLAGS) \
|
||||
-I$(top_srcdir)/src
|
||||
tests_test_fuzz_resolver_LDADD = $(STROPHE_LIBS)
|
||||
tests_test_fuzz_resolver_LDFLAGS = -static
|
||||
endif
|
||||
|
||||
tests_check_parser_SOURCES = tests/check_parser.c tests/test.h
|
||||
tests_check_parser_CFLAGS = $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
|
||||
-I$(top_srcdir)/src
|
||||
@@ -203,6 +281,16 @@ tests_test_sha512_CFLAGS = -I$(top_srcdir)/src
|
||||
tests_test_md5_SOURCES = tests/test_md5.c tests/test.c src/md5.c
|
||||
tests_test_md5_CFLAGS = -I$(top_srcdir)/src
|
||||
|
||||
tests_test_send_queue_SOURCES = tests/test_send_queue.c
|
||||
tests_test_send_queue_CFLAGS = -I$(top_srcdir)/src
|
||||
tests_test_send_queue_LDADD = $(STROPHE_LIBS)
|
||||
tests_test_send_queue_LDFLAGS = -static
|
||||
|
||||
tests_test_serialize_sm_SOURCES = tests/test_serialize_sm.c tests/test.c tests/test.h
|
||||
tests_test_serialize_sm_CFLAGS = -I$(top_srcdir)/src
|
||||
tests_test_serialize_sm_LDADD = $(STROPHE_LIBS)
|
||||
tests_test_serialize_sm_LDFLAGS = -static
|
||||
|
||||
tests_test_snprintf_SOURCES = tests/test_snprintf.c
|
||||
tests_test_snprintf_CFLAGS = -I$(top_srcdir)/src
|
||||
|
||||
@@ -211,12 +299,56 @@ tests_test_string_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||
tests_test_string_LDADD = $(STROPHE_LIBS)
|
||||
tests_test_string_LDFLAGS = -static
|
||||
|
||||
tests_test_stanza_SOURCES = tests/test_stanza.c
|
||||
tests_test_stanza_CFLAGS = $(STROPHE_FLAGS)
|
||||
tests_test_stanza_SOURCES = tests/test_stanza.c tests/test.h
|
||||
tests_test_stanza_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||
tests_test_stanza_LDADD = $(STROPHE_LIBS)
|
||||
tests_test_stanza_LDFLAGS = -static
|
||||
|
||||
tests_test_xmppaddr_SOURCES = tests/test_xmppaddr.c
|
||||
tests_test_xmppaddr_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||
tests_test_xmppaddr_LDADD = $(STROPHE_LIBS)
|
||||
tests_test_xmppaddr_LDFLAGS = -static
|
||||
|
||||
format:
|
||||
@echo " * run clang-format on all sources"
|
||||
@dos2unix -q src/*.[ch] *.h tests/*.[ch] examples/*.c
|
||||
@clang-format -i src/*.[ch] *.h tests/*.[ch] examples/*.c
|
||||
@dos2unix -k -q $(top_srcdir)/src/*.[ch] $(top_srcdir)/*.h $(top_srcdir)/tests/*.[ch] $(top_srcdir)/examples/*.c
|
||||
@clang-format -i $(top_srcdir)/src/*.[ch] $(top_srcdir)/*.h $(top_srcdir)/tests/*.[ch] $(top_srcdir)/examples/*.c
|
||||
|
||||
if COVERAGE
|
||||
MOSTLYCLEANFILES = src/*.gcno src/*.gcda coverage.info
|
||||
clean-local:
|
||||
-rm -rf coverage/
|
||||
|
||||
coverage: check
|
||||
@lcov --capture --no-external --directory src -q --output-file coverage.info
|
||||
@genhtml coverage.info --output-directory coverage -q
|
||||
endif
|
||||
|
||||
release: test-release-all
|
||||
|
||||
dist-archives:
|
||||
$(MAKE) dist
|
||||
$(MAKE) dist-bzip2
|
||||
$(MAKE) dist-xz
|
||||
|
||||
test-release: dist
|
||||
@touch testbuild-$(PACKAGE_VERSION).log && ln -sf testbuild-$(PACKAGE_VERSION).log testbuild.log
|
||||
@touch testerr-$(PACKAGE_VERSION).log && ln -sf testerr-$(PACKAGE_VERSION).log testerr.log
|
||||
@mkdir -p test-release && cp $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.* test-release && pushd test-release && \
|
||||
tar xzf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||
echo "Success" && popd
|
||||
|
||||
test-release-all: dist-archives
|
||||
@touch testbuild-$(PACKAGE_VERSION).log && ln -sf testbuild-$(PACKAGE_VERSION).log testbuild.log
|
||||
@mkdir -p test-release && cp $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.* test-release && pushd test-release && \
|
||||
tar xJf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.xz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||
tar xjf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||
tar xzf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||
echo "Success" && popd
|
||||
|
||||
docs:
|
||||
@SRCDIR=$(top_srcdir) doxygen -q $(top_srcdir)/Doxyfile
|
||||
|
||||
@VALGRIND_CHECK_RULES@
|
||||
|
||||
.PHONY: docs test-release
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
libstrophe [](https://travis-ci.org/strophe/libstrophe)
|
||||
libstrophe [](https://github.com/strophe/libstrophe/actions/workflows/main.yml?query=branch%3Amaster+++)
|
||||
==========
|
||||
|
||||
libstrophe is a lightweight XMPP client library written in C. It has
|
||||
minimal dependencies and is configurable for various environments. It
|
||||
runs well on Linux, Unix and Windows based platforms.
|
||||
|
||||
libstrophe is dual licensed under MIT and GPLv3.
|
||||
libstrophe is dual licensed under MIT or GPLv3.
|
||||
|
||||
Build Instructions
|
||||
------------------
|
||||
@@ -42,17 +42,30 @@ another path use the `--prefix` option during configure, e.g.:
|
||||
Run script `build-android.sh` and follow the instructions. You will
|
||||
need expat sources and android-ndk.
|
||||
|
||||
### Code Coverage
|
||||
|
||||
If you want to create a code coverage report, run:
|
||||
|
||||
./configure --enable-coverage
|
||||
make coverage
|
||||
|
||||
The coverage report can be found in `./coverage/index.html`.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
libstrophe requires:
|
||||
|
||||
- expat or libxml2 - expat is the default; use --with-libxml2 to
|
||||
- expat or libxml2 - expat is the default; use `--with-libxml2` to
|
||||
switch
|
||||
- openssl on UNIX systems
|
||||
- openssl or GnuTLS on UNIX systems - openssl is default; use
|
||||
`--with-gnutls` to switch
|
||||
|
||||
To build libstrophe using autotools you will need autoconf, automake,
|
||||
libtool and pkg-config.
|
||||
To build libstrophe using autotools you will need `autoconf`,
|
||||
`automake`, `libtool` and `pkg-config`.
|
||||
|
||||
To run code coverage analysis you will need `gcov` and `lcov`.
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -69,4 +82,17 @@ by running:
|
||||
|
||||
doxygen
|
||||
|
||||
or if you have everything configured properly:
|
||||
|
||||
make docs
|
||||
|
||||
Then open `docs/html/index.html`.
|
||||
|
||||
An online version of the documentation of the latest release is available on https://strophe.im/libstrophe/
|
||||
|
||||
Releases
|
||||
--------
|
||||
|
||||
Releases are signed with the GPG key with ID `F8ADC1F9A68A7AFF0E2C89E4391A5EFC2D1709DE`.
|
||||
|
||||
It can be found e.g. on https://keys.openpgp.org/
|
||||
|
||||
@@ -8,6 +8,7 @@ BinPackParameters: 'false'
|
||||
BreakBeforeBraces: Linux
|
||||
ColumnLimit: '80'
|
||||
DerivePointerAlignment: 'false'
|
||||
IndentGotoLabels: false
|
||||
IndentWidth: '4'
|
||||
PointerAlignment: Right
|
||||
SortIncludes: 'false'
|
||||
|
||||
163
configure.ac
163
configure.ac
@@ -1,4 +1,13 @@
|
||||
AC_INIT([libstrophe], [0.10.0], [jack@metajack.im])
|
||||
m4_define([v_maj], [0])
|
||||
m4_define([v_min], [14])
|
||||
m4_define([v_patch], [0])
|
||||
m4_define([project_version], [v_maj.v_min.v_patch])
|
||||
|
||||
m4_define([lt_cur], m4_eval(v_maj + v_min))
|
||||
m4_define([lt_rev], v_patch)
|
||||
m4_define([lt_age], v_min)
|
||||
|
||||
AC_INIT([libstrophe], [project_version], [jack@metajack.im])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
LT_INIT([dlopen])
|
||||
@@ -9,13 +18,16 @@ PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
AS_CASE([$host_os],
|
||||
[dragonfly*], [PLATFORM="bsd"],
|
||||
[freebsd*], [PLATFORM="bsd"],
|
||||
[openbsd*], [PLATFORM="bsd"],
|
||||
[netbsd*], [PLATFORM="bsd"],
|
||||
[openbsd*], [PLATFORM="bsd"],
|
||||
[*nto*|*qnx*], [PLATFORM="qnx"],
|
||||
[*solaris*], [PLATFORM="solaris"],
|
||||
[*android*], [PLATFORM="android"],
|
||||
[*haiku*], [PLATFORM="haiku"],
|
||||
[*mingw*], [PLATFORM="win32"
|
||||
MINGW_LIBS="-lws2_32"],
|
||||
[PLATFORM="nix"])
|
||||
|
||||
WARNING_FLAGS="-Wall"
|
||||
@@ -24,18 +36,111 @@ AS_CASE([$PLATFORM],
|
||||
[haiku], [],
|
||||
[WARNING_FLAGS="$WARNING_FLAGS -Wextra"])
|
||||
|
||||
AC_ARG_ENABLE([examples],
|
||||
[AS_HELP_STRING([--disable-examples], [turn off examples])],
|
||||
[case "${enableval}" in yes) examples=true ;; no) examples=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --disable-examples]) ;; esac],[examples=true])
|
||||
AM_CONDITIONAL([EXAMPLES], [test x$examples = xtrue])
|
||||
AC_ARG_WITH([libxml2],
|
||||
[AS_HELP_STRING([--with-libxml2], [use libxml2 for XML parsing, expat is the default])])
|
||||
AC_ARG_WITH([gnutls],
|
||||
[AS_HELP_STRING([--with-gnutls], [use GnuTLS for TLS support, OpenSSL is the default])])
|
||||
AC_ARG_WITH([schannel],
|
||||
[AS_HELP_STRING([--with-schannel], [use Windows Schannel for TLS support, OpenSSL is the default])])
|
||||
AC_ARG_ENABLE([tls],
|
||||
[AS_HELP_STRING([--disable-tls], [disable TLS support])])
|
||||
AC_ARG_ENABLE([cares],
|
||||
[AS_HELP_STRING([--enable-cares], [use c-ares for DNS resolution])])
|
||||
AC_ARG_ENABLE([getrandom],
|
||||
[AS_HELP_STRING([--disable-getrandom], [disable usage of the getrandom() system call])])
|
||||
AC_ARG_ENABLE([zlib],
|
||||
[AS_HELP_STRING([--disable-zlib], [disable compression support])])
|
||||
|
||||
AC_ARG_ENABLE([fuzzing],
|
||||
[AS_HELP_STRING([--enable-fuzzing], [turn on fuzzing test])],
|
||||
[case "${enableval}" in yes) fuzzing=true ;; no) fuzzing=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-fuzzing]) ;; esac],[fuzzing=false])
|
||||
AM_CONDITIONAL([FUZZ], [test x$fuzzing = xtrue])
|
||||
|
||||
if test "x$enable_fuzzing" = "xyes" ; then
|
||||
if test "x$CC" != "xclang" ; then
|
||||
AC_MSG_ERROR(["You need to set CC=clang to use --enable-fuzzing, used $CC"])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([coverage],
|
||||
[AS_HELP_STRING([--enable-coverage], [turn on coverage for tests])],
|
||||
[case "${enableval}" in yes) coverage=true ;; no) coverage=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-coverage]) ;; esac],[coverage=false])
|
||||
AM_CONDITIONAL([COVERAGE], [test x$coverage = xtrue])
|
||||
|
||||
if test "$enable_coverage" = "yes"; then
|
||||
COVERAGE_CFLAGS="--coverage -g"
|
||||
COVERAGE_LDFLAGS="--coverage -lgcov"
|
||||
COVERAGE_PRE="-Wl,--whole-archive"
|
||||
COVERAGE_POST="-Wl,--no-whole-archive"
|
||||
else
|
||||
COVERAGE_CFLAGS=""
|
||||
COVERAGE_LDFLAGS=""
|
||||
COVERAGE_PRE=""
|
||||
COVERAGE_POST=""
|
||||
fi
|
||||
|
||||
m4_include([m4/ax_valgrind_check.m4])
|
||||
AX_VALGRIND_DFLT([drd], [off])
|
||||
AX_VALGRIND_DFLT([helgrind], [off])
|
||||
AX_VALGRIND_DFLT([sgcheck], [off])
|
||||
AX_VALGRIND_CHECK
|
||||
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt], AC_DEFINE(HAVE_CLOCK_GETTIME,1))
|
||||
AC_SEARCH_LIBS([socket], [network socket])
|
||||
AC_CHECK_FUNCS([snprintf vsnprintf])
|
||||
AC_CHECK_DECLS([va_copy], [], [], [#include <stdarg.h>])
|
||||
AC_CHECK_FUNCS([snprintf vsnprintf], [], [have_snprintf=no])
|
||||
|
||||
if test "x$enable_tls" != xno; then
|
||||
dnl Checking for va_copy availability
|
||||
AC_MSG_CHECKING([for va_copy])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdarg.h>
|
||||
va_list ap1,ap2;]], [[va_copy(ap1,ap2);]])],
|
||||
have_va_copy=yes,
|
||||
have_va_copy=no)
|
||||
AC_MSG_RESULT($have_va_copy)
|
||||
if test x"$have_va_copy" = x"yes"; then
|
||||
AC_DEFINE(HAVE_VA_COPY,1,[Whether va_copy() is available])
|
||||
else
|
||||
AC_MSG_CHECKING([for __va_copy])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdarg.h>
|
||||
va_list ap1,ap2;]], [[__va_copy(ap1,ap2);]])],
|
||||
have___va_copy=yes,
|
||||
have___va_copy=no)
|
||||
AC_MSG_RESULT($have___va_copy)
|
||||
if test x"$have___va_copy" = x"yes"; then
|
||||
AC_DEFINE(HAVE___VA_COPY,1,[Whether __va_copy() is available])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$have_va_copy$have___va_copy" = x"nono"; then
|
||||
dnl Checking whether va_list is an array type
|
||||
AC_MSG_CHECKING([whether va_list is an array type])
|
||||
AC_TRY_COMPILE2([
|
||||
#include <stdarg.h>
|
||||
void a(va_list * ap) {}],[
|
||||
va_list ap1, ap2; a(&ap1); ap2 = (va_list) ap1],[
|
||||
AC_MSG_RESULT(no)],[
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE([VA_LIST_IS_ARRAY], [1],[Define if va_list is an array type])])
|
||||
fi
|
||||
|
||||
if test "x$enable_tls" != xno -a "x$with_gnutls" = xyes; then
|
||||
PKG_CHECK_MODULES([gnutls], [gnutls],
|
||||
[PC_REQUIRES="gnutls ${PC_REQUIRES}"],
|
||||
[AC_CHECK_HEADER([gnutls/gnutls.h],
|
||||
[
|
||||
gnutls_LIBS="-lgnutls"
|
||||
PC_LIBS="${gnutls_LIBS} ${PC_LIBS}"
|
||||
],
|
||||
[AC_MSG_ERROR([gnutls not found; gnutls required])]
|
||||
)])
|
||||
elif test "x$enable_tls" != xno -a "x$with_schannel" = xyes; then
|
||||
if test "x$PLATFORM" != xwin32; then
|
||||
AC_MSG_ERROR([schannel is only supported on Windows])
|
||||
fi
|
||||
elif test "x$enable_tls" != xno; then
|
||||
PKG_CHECK_MODULES([openssl], [openssl],
|
||||
[PC_REQUIRES="openssl ${PC_REQUIRES}"],
|
||||
[AC_CHECK_HEADER([openssl/ssl.h],
|
||||
@@ -47,6 +152,12 @@ if test "x$enable_tls" != xno; then
|
||||
)])
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS([getrandom], [], [enable_getrandom=no])
|
||||
if test "$enable_getrandom" = "no"; then
|
||||
AC_DEFINE(DONT_USE_GETRANDOM)
|
||||
AC_MSG_NOTICE([libstrophe will not use the getrandom() system call])
|
||||
fi
|
||||
|
||||
with_parser=""
|
||||
if test "x$with_libxml2" != xyes; then
|
||||
PKG_CHECK_MODULES([expat], [expat >= 2.0.0],
|
||||
@@ -113,8 +224,10 @@ AS_CASE([$PLATFORM],
|
||||
[solaris], [RESOLV_LIBS="-lresolv -lsocket -lnsl"],
|
||||
[android], [RESOLV_LIBS=""],
|
||||
[haiku], [RESOLV_LIBS="-lnetwork"],
|
||||
[win32], [RESOLV_LIBS=""],
|
||||
[RESOLV_LIBS="-lresolv"])
|
||||
|
||||
if test "x$PLATFORM" != xwin32; then
|
||||
LIBS_TMP="${LIBS}"
|
||||
LIBS="${RESOLV_LIBS}"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
@@ -133,9 +246,22 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
[AC_MSG_ERROR([res_query() not found with LIBS="${LIBS}"])])
|
||||
LIBS="${LIBS_TMP}"
|
||||
PC_LIBS="${RESOLV_LIBS} ${PC_LIBS}"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if test "x$enable_zlib" != xno; then
|
||||
PKG_CHECK_MODULES([zlib], [zlib >= 1.2.0],
|
||||
[
|
||||
PC_REQUIRES="zlib ${PC_REQUIRES}"
|
||||
ZLIB_CFLAGS=$zlib_CFLAGS
|
||||
ZLIB_LIBS=$zlib_LIBS
|
||||
AC_DEFINE([HAVE_ZLIB])
|
||||
],
|
||||
[AC_MSG_ERROR([zlib not found])])
|
||||
# TODO: if pkg-config doesn't find, check the library manually
|
||||
fi
|
||||
|
||||
m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
|
||||
[AC_ARG_WITH([pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-pkgconfigdir],
|
||||
@@ -144,16 +270,43 @@ m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
|
||||
AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])])
|
||||
|
||||
AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
|
||||
AM_CONDITIONAL([DISABLE_COMPRESSION], [test x$enable_zlib = xno])
|
||||
AM_CONDITIONAL([DISABLE_TLS], [test x$enable_tls = xno])
|
||||
AM_CONDITIONAL([DISABLE_STATIC], [test x$enable_static = xno])
|
||||
AM_CONDITIONAL([NEED_SNPRINTF], [test x$have_snprintf = xno])
|
||||
AM_CONDITIONAL([TLS_WITH_GNUTLS], [test x$with_gnutls = xyes])
|
||||
AM_CONDITIONAL([TLS_WITH_SCHANNEL], [test x$with_schannel = xyes])
|
||||
|
||||
# define while compiling
|
||||
AC_DEFINE_UNQUOTED(LIBXMPP_VERSION_MAJOR, [v_maj], [Major version])
|
||||
AC_DEFINE_UNQUOTED(LIBXMPP_VERSION_MINOR, [v_min], [Minor version])
|
||||
|
||||
VMAJ=v_maj
|
||||
VMIN=v_min
|
||||
AC_SUBST(VMAJ)
|
||||
AC_SUBST(VMIN)
|
||||
# define in package-config file
|
||||
PC_CFLAGS="${PC_CFLAGS} -DLIBXMPP_VERSION_MAJOR=${VMAJ} -DLIBXMPP_VERSION_MINOR=${VMIN}"
|
||||
|
||||
# set the SO version of the installed library
|
||||
VERSION_INFO="lt_cur:lt_rev:lt_age"
|
||||
AC_SUBST([VERSION_INFO])
|
||||
|
||||
AC_SUBST([PC_REQUIRES], [${PC_REQUIRES}])
|
||||
AC_SUBST([PC_CFLAGS], [${PC_CFLAGS}])
|
||||
AC_SUBST([PC_LIBS], [${PC_LIBS}])
|
||||
|
||||
AC_SUBST(COVERAGE_CFLAGS)
|
||||
AC_SUBST(COVERAGE_LDFLAGS)
|
||||
AC_SUBST(COVERAGE_PRE)
|
||||
AC_SUBST(COVERAGE_POST)
|
||||
AC_SUBST(MINGW_LIBS)
|
||||
AC_SUBST(PARSER_CFLAGS)
|
||||
AC_SUBST(PARSER_LIBS)
|
||||
AC_SUBST(RESOLV_CFLAGS)
|
||||
AC_SUBST(RESOLV_LIBS)
|
||||
AC_SUBST(WARNING_FLAGS)
|
||||
AC_SUBST(ZLIB_CFLAGS)
|
||||
AC_SUBST(ZLIB_LIBS)
|
||||
AC_CONFIG_FILES([Makefile libstrophe.pc])
|
||||
AC_OUTPUT
|
||||
|
||||
0
docs/.keep
Normal file
0
docs/.keep
Normal file
@@ -1,2 +0,0 @@
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* active.c
|
||||
** libstrophe XMPP client library -- basic usage example
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* This example demonstrates basic handler functions by printing out
|
||||
@@ -19,9 +20,7 @@
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
int handle_reply(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
int handle_reply(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
xmpp_stanza_t *query, *item;
|
||||
const char *type;
|
||||
@@ -46,11 +45,11 @@ int handle_reply(xmpp_conn_t *const conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_handler(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t status,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata)
|
||||
void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
xmpp_stanza_t *iq, *query;
|
||||
|
||||
102
examples/basic.c
102
examples/basic.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* basic.c
|
||||
** libstrophe XMPP client library -- basic usage example
|
||||
**
|
||||
@@ -6,36 +7,29 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
/* hardcoded TCP keepalive timeout and interval */
|
||||
#define KA_TIMEOUT 60
|
||||
#define KA_INTERVAL 1
|
||||
|
||||
/* define a handler for connection events */
|
||||
void conn_handler(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t status,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata)
|
||||
static void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
int secured;
|
||||
|
||||
(void)error;
|
||||
(void)stream_error;
|
||||
|
||||
if (status == XMPP_CONN_CONNECT) {
|
||||
fprintf(stderr, "DEBUG: connected\n");
|
||||
secured = xmpp_conn_is_secured(conn);
|
||||
fprintf(stderr, "DEBUG: connection is %s.\n",
|
||||
secured ? "secured" : "NOT secured");
|
||||
xmpp_disconnect(conn);
|
||||
} else {
|
||||
fprintf(stderr, "DEBUG: disconnected\n");
|
||||
@@ -43,20 +37,39 @@ void conn_handler(xmpp_conn_t *const conn,
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(int exit_code)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: basic [options] [<host> [<port>]]\n\n"
|
||||
"Options:\n"
|
||||
" --jid <jid> The JID to use to authenticate.\n"
|
||||
" --pass <pass> The password of the JID.\n"
|
||||
" --disable-tls Disable TLS.\n"
|
||||
" --mandatory-tls Deny plaintext connection.\n"
|
||||
" --trust-tls Trust TLS certificate.\n"
|
||||
" --legacy-ssl Use old style SSL.\n"
|
||||
" --legacy-auth Allow legacy authentication.\n"
|
||||
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||
"--legacy-ssl\n");
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
xmpp_log_t *log;
|
||||
char *jid, *pass, *host = NULL;
|
||||
const char *cafile = NULL;
|
||||
char *jid = NULL, *password = NULL, *host = NULL;
|
||||
long flags = 0;
|
||||
int tcp_keepalive = 0;
|
||||
int i;
|
||||
unsigned long port = 0;
|
||||
|
||||
/* take a jid and password on the command line */
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--disable-tls") == 0)
|
||||
if (strcmp(argv[i], "--help") == 0)
|
||||
usage(0);
|
||||
else if (strcmp(argv[i], "--disable-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||
else if (strcmp(argv[i], "--mandatory-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
|
||||
@@ -66,32 +79,21 @@ int main(int argc, char **argv)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
|
||||
else if (strcmp(argv[i], "--legacy-auth") == 0)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||
else if (strcmp(argv[i], "--tcp-keepalive") == 0)
|
||||
tcp_keepalive = 1;
|
||||
else if (strncmp(argv[i], "--cafile=", strlen("--cafile=")) == 0)
|
||||
cafile = argv[i] + strlen("--cafile=");
|
||||
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
|
||||
jid = argv[i];
|
||||
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
|
||||
password = argv[i];
|
||||
else
|
||||
break;
|
||||
}
|
||||
if ((argc - i) < 2 || (argc - i) > 3) {
|
||||
fprintf(stderr, "Usage: basic [options] <jid> <pass> [<host>]\n\n"
|
||||
"Options:\n"
|
||||
" --cafile=<PATH> CAfile for cert verification.\n"
|
||||
" --disable-tls Disable TLS.\n"
|
||||
" --mandatory-tls Deny plaintext connection.\n"
|
||||
" --trust-tls Trust TLS certificate.\n"
|
||||
" --legacy-ssl Use old style SSL.\n"
|
||||
" --legacy-auth Allow legacy authentication.\n"
|
||||
" --tcp-keepalive Configure TCP keepalive.\n\n"
|
||||
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||
"--legacy-ssl\n");
|
||||
return 1;
|
||||
if ((!jid) || (argc - i) > 2) {
|
||||
usage(1);
|
||||
}
|
||||
|
||||
jid = argv[i];
|
||||
pass = argv[i + 1];
|
||||
if (i + 2 < argc)
|
||||
host = argv[i + 2];
|
||||
if (i < argc)
|
||||
host = argv[i];
|
||||
if (i + 1 < argc)
|
||||
port = strtoul(argv[i + 1], NULL, 0);
|
||||
|
||||
/*
|
||||
* Note, this example doesn't handle errors. Applications should check
|
||||
@@ -111,24 +113,20 @@ int main(int argc, char **argv)
|
||||
|
||||
/* configure connection properties (optional) */
|
||||
xmpp_conn_set_flags(conn, flags);
|
||||
/* configure TCP keepalive (optional) */
|
||||
if (tcp_keepalive)
|
||||
xmpp_conn_set_keepalive(conn, KA_TIMEOUT, KA_INTERVAL);
|
||||
|
||||
/* add external CAfile if needed */
|
||||
if (cafile)
|
||||
xmpp_conn_tls_cafile(conn, cafile);
|
||||
|
||||
/* setup authentication information */
|
||||
xmpp_conn_set_jid(conn, jid);
|
||||
xmpp_conn_set_pass(conn, pass);
|
||||
if (jid)
|
||||
xmpp_conn_set_jid(conn, jid);
|
||||
if (password)
|
||||
xmpp_conn_set_pass(conn, password);
|
||||
|
||||
/* initiate connection */
|
||||
xmpp_connect_client(conn, host, 0, conn_handler, ctx);
|
||||
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
|
||||
|
||||
/* enter the event loop -
|
||||
our connect handler will trigger an exit */
|
||||
xmpp_run(ctx);
|
||||
/* enter the event loop -
|
||||
our connect handler will trigger an exit */
|
||||
xmpp_run(ctx);
|
||||
}
|
||||
|
||||
/* release our connection and context */
|
||||
xmpp_conn_release(conn);
|
||||
|
||||
212
examples/bot.c
212
examples/bot.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* bot.c
|
||||
** libstrophe XMPP client library -- basic usage example
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* simple bot example
|
||||
@@ -19,12 +20,18 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <conio.h>
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
int version_handler(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int reconnect;
|
||||
|
||||
int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
xmpp_stanza_t *reply, *query, *name, *version, *text;
|
||||
const char *ns;
|
||||
@@ -70,15 +77,19 @@ int version_handler(xmpp_conn_t *const conn,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int message_handler(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int _quit_handler(xmpp_conn_t *conn, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
xmpp_disconnect(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
xmpp_stanza_t *body, *reply;
|
||||
const char *type;
|
||||
char *intext, *replytext;
|
||||
int quit = 0;
|
||||
|
||||
body = xmpp_stanza_get_child_by_name(stanza, "body");
|
||||
if (body == NULL)
|
||||
@@ -98,7 +109,11 @@ int message_handler(xmpp_conn_t *const conn,
|
||||
|
||||
if (strcmp(intext, "quit") == 0) {
|
||||
replytext = strdup("bye!");
|
||||
quit = 1;
|
||||
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
|
||||
} else if (strcmp(intext, "reconnect") == 0) {
|
||||
replytext = strdup("alright, let's see what happens!");
|
||||
reconnect = 1;
|
||||
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
|
||||
} else {
|
||||
replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1);
|
||||
strcpy(replytext, intext);
|
||||
@@ -111,18 +126,15 @@ int message_handler(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_release(reply);
|
||||
free(replytext);
|
||||
|
||||
if (quit)
|
||||
xmpp_disconnect(conn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* define a handler for connection events */
|
||||
void conn_handler(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t status,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata)
|
||||
void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
|
||||
@@ -146,21 +158,123 @@ void conn_handler(xmpp_conn_t *const conn,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static char *getpassword(const char *prompt, size_t maxlen)
|
||||
{
|
||||
char *b, *buffer = malloc(maxlen);
|
||||
size_t i = 0;
|
||||
|
||||
b = buffer;
|
||||
fputs(prompt, stderr);
|
||||
for (i = 0; i < maxlen; i++, b++) {
|
||||
char c = _getch();
|
||||
if (c == '\r' || c == '\n')
|
||||
break;
|
||||
*b = c;
|
||||
}
|
||||
*b = '\0';
|
||||
fputs("\n", stderr);
|
||||
return buffer;
|
||||
}
|
||||
#else
|
||||
#define getpassword(prompt, maxlen) getpass(prompt)
|
||||
#endif
|
||||
|
||||
static int
|
||||
password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
printf("Trying to unlock %s\n", xmpp_conn_get_keyfile(conn));
|
||||
char *pass = getpassword("Please enter password: ", pw_max);
|
||||
if (!pass)
|
||||
return -1;
|
||||
size_t passlen = strlen(pass);
|
||||
int ret;
|
||||
if (passlen >= pw_max) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
ret = passlen + 1;
|
||||
memcpy(pw, pass, ret);
|
||||
out:
|
||||
memset(pass, 0, passlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usage(int exit_code)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: bot [options] <jid> <pass>\n\n"
|
||||
"Options:\n"
|
||||
" --jid <jid> The JID to use to authenticate.\n"
|
||||
" --pass <pass> The password of the JID.\n"
|
||||
" --tls-cert <cert> Path to client certificate.\n"
|
||||
" --tls-key <key> Path to private key or P12 file.\n\n"
|
||||
" --tcp-keepalive Configure TCP keepalive.\n"
|
||||
" --disable-tls Disable TLS.\n"
|
||||
" --mandatory-tls Deny plaintext connection.\n"
|
||||
" --trust-tls Trust TLS certificate.\n"
|
||||
" --legacy-ssl Use old style SSL.\n"
|
||||
" --legacy-auth Allow legacy authentication.\n"
|
||||
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||
"--legacy-ssl\n"
|
||||
" --zlib Enable compression via zlib.\n"
|
||||
" --dont-reset When using zlib, don't do a full-flush "
|
||||
"after compression.\n");
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
xmpp_log_t *log;
|
||||
char *jid, *pass;
|
||||
xmpp_sm_state_t *sm_state = NULL;
|
||||
char *jid = NULL, *password = NULL, *host = NULL, *cert = NULL, *key = NULL;
|
||||
long flags = 0;
|
||||
int i, tcp_keepalive = 0;
|
||||
unsigned long port = 0;
|
||||
|
||||
/* take a jid and password on the command line */
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: bot <jid> <pass>\n\n");
|
||||
return 1;
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--help") == 0)
|
||||
usage(0);
|
||||
else if (strcmp(argv[i], "--disable-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||
else if (strcmp(argv[i], "--mandatory-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
|
||||
else if (strcmp(argv[i], "--trust-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_TRUST_TLS;
|
||||
else if (strcmp(argv[i], "--legacy-ssl") == 0)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
|
||||
else if (strcmp(argv[i], "--legacy-auth") == 0)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||
else if (strcmp(argv[i], "--zlib") == 0)
|
||||
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
|
||||
else if (strcmp(argv[i], "--dont-reset") == 0)
|
||||
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
|
||||
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
|
||||
jid = argv[i];
|
||||
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
|
||||
password = argv[i];
|
||||
else if ((strcmp(argv[i], "--tls-cert") == 0) && (++i < argc))
|
||||
cert = argv[i];
|
||||
else if ((strcmp(argv[i], "--tls-key") == 0) && (++i < argc))
|
||||
key = argv[i];
|
||||
else if (strcmp(argv[i], "--tcp-keepalive") == 0)
|
||||
tcp_keepalive = 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if ((!jid && !key) || (argc - i) > 2) {
|
||||
usage(1);
|
||||
}
|
||||
|
||||
jid = argv[1];
|
||||
pass = argv[2];
|
||||
if (i < argc)
|
||||
host = argv[i];
|
||||
if (i + 1 < argc)
|
||||
port = strtoul(argv[i + 1], NULL, 0);
|
||||
|
||||
/* init library */
|
||||
xmpp_initialize();
|
||||
@@ -170,29 +284,55 @@ int main(int argc, char **argv)
|
||||
/* create a context */
|
||||
ctx = xmpp_ctx_new(NULL, log);
|
||||
|
||||
create_connection:
|
||||
reconnect = 0;
|
||||
/* create a connection */
|
||||
conn = xmpp_conn_new(ctx);
|
||||
|
||||
/*
|
||||
* also you can disable TLS support or force legacy SSL
|
||||
* connection without STARTTLS
|
||||
*
|
||||
* see xmpp_conn_set_flags() or examples/basic.c
|
||||
*/
|
||||
/* configure connection properties (optional) */
|
||||
xmpp_conn_set_flags(conn, flags);
|
||||
|
||||
/* ask for a password if key is protected */
|
||||
xmpp_conn_set_password_callback(conn, password_callback, NULL);
|
||||
/* try at max 3 times in case the user enters the password wrong */
|
||||
xmpp_conn_set_password_retries(conn, 3);
|
||||
/* setup authentication information */
|
||||
xmpp_conn_set_jid(conn, jid);
|
||||
xmpp_conn_set_pass(conn, pass);
|
||||
if (key)
|
||||
xmpp_conn_set_client_cert(conn, cert, key);
|
||||
if (jid)
|
||||
xmpp_conn_set_jid(conn, jid);
|
||||
if (password)
|
||||
xmpp_conn_set_pass(conn, password);
|
||||
|
||||
/* enable TCP keepalive, using canned callback function */
|
||||
if (tcp_keepalive)
|
||||
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive);
|
||||
|
||||
/* set Stream-Mangement state if available */
|
||||
if (sm_state) {
|
||||
xmpp_conn_set_sm_state(conn, sm_state);
|
||||
sm_state = NULL;
|
||||
}
|
||||
|
||||
/* initiate connection */
|
||||
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
|
||||
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
|
||||
|
||||
/* enter the event loop -
|
||||
our connect handler will trigger an exit */
|
||||
xmpp_run(ctx);
|
||||
/* enter the event loop -
|
||||
our connect handler will trigger an exit */
|
||||
xmpp_run(ctx);
|
||||
}
|
||||
|
||||
/* release our connection and context */
|
||||
/* save the Stream-Mangement state if we should re-connect */
|
||||
if (reconnect)
|
||||
sm_state = xmpp_conn_get_sm_state(conn);
|
||||
|
||||
/* release our connection */
|
||||
xmpp_conn_release(conn);
|
||||
|
||||
if (reconnect)
|
||||
goto create_connection;
|
||||
|
||||
/* release our context */
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
/* final shutdown of the library */
|
||||
|
||||
372
examples/complex.c
Normal file
372
examples/complex.c
Normal file
@@ -0,0 +1,372 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* complex.c
|
||||
** libstrophe XMPP client library -- more complex usage example
|
||||
**
|
||||
** Copyright (C) 2005-2009 Collecta, Inc.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <conio.h>
|
||||
#include <ctype.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <mstcpip.h> /* tcp_keepalive */
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
/* hardcoded TCP keepalive timeout and interval */
|
||||
#define KA_TIMEOUT 60
|
||||
#define KA_INTERVAL 30
|
||||
#define KA_COUNT 3
|
||||
#define USER_TIMEOUT 150
|
||||
|
||||
static void print_tlscert(const xmpp_tlscert_t *cert)
|
||||
{
|
||||
const char *name;
|
||||
size_t n;
|
||||
for (n = 0; n < (unsigned)XMPP_CERT_ELEMENT_MAX; ++n) {
|
||||
printf("\t%32s: %s\n", xmpp_tlscert_get_description(n),
|
||||
xmpp_tlscert_get_string(cert, n));
|
||||
}
|
||||
n = 0;
|
||||
while ((name = xmpp_tlscert_get_dnsname(cert, n++)) != NULL)
|
||||
printf("\t%32s: %s\n", "dnsName", name);
|
||||
printf("PEM:\n%s\n", xmpp_tlscert_get_pem(cert));
|
||||
}
|
||||
|
||||
/* define a handler for connection events */
|
||||
static void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
int secured;
|
||||
|
||||
(void)error;
|
||||
(void)stream_error;
|
||||
|
||||
if (status == XMPP_CONN_CONNECT) {
|
||||
fprintf(stderr, "DEBUG: connected\n");
|
||||
secured = xmpp_conn_is_secured(conn);
|
||||
fprintf(stderr, "DEBUG: connection is %s.\n",
|
||||
secured ? "secured" : "NOT secured");
|
||||
if (secured) {
|
||||
xmpp_tlscert_t *cert = xmpp_conn_get_peer_cert(conn);
|
||||
print_tlscert(cert);
|
||||
xmpp_tlscert_free(cert);
|
||||
}
|
||||
xmpp_disconnect(conn);
|
||||
} else {
|
||||
fprintf(stderr, "DEBUG: disconnected\n");
|
||||
xmpp_stop(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int certfail_handler(const xmpp_tlscert_t *cert,
|
||||
const char *const errormsg)
|
||||
{
|
||||
char read_char[16] = {0};
|
||||
printf("Received certificate can't be validated!\n");
|
||||
printf("Reason: %s\n", errormsg);
|
||||
print_tlscert(cert);
|
||||
printf("Do you agree to connect?\n[y(es)|n(o)]: ");
|
||||
fflush(stdout);
|
||||
if (fgets(read_char, sizeof(read_char), stdin) == NULL) {
|
||||
printf("fgets() failed\n");
|
||||
return 0;
|
||||
}
|
||||
printf("\n");
|
||||
return read_char[0] == 'y' || read_char[0] == 'Y';
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static char *getpassword(const char *prompt, size_t maxlen)
|
||||
{
|
||||
char *b, *buffer = malloc(maxlen);
|
||||
size_t i = 0;
|
||||
|
||||
b = buffer;
|
||||
fputs(prompt, stderr);
|
||||
for (i = 0; i < maxlen; i++, b++) {
|
||||
char c = _getch();
|
||||
if (c == '\r' || c == '\n')
|
||||
break;
|
||||
*b = c;
|
||||
}
|
||||
*b = '\0';
|
||||
fputs("\n", stderr);
|
||||
return buffer;
|
||||
}
|
||||
#else
|
||||
#define getpassword(prompt, maxlen) getpass(prompt)
|
||||
#endif
|
||||
|
||||
static int
|
||||
password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
printf("Trying to unlock %s\n", xmpp_conn_get_keyfile(conn));
|
||||
char *pass = getpassword("Please enter password: ", pw_max);
|
||||
if (!pass)
|
||||
return -1;
|
||||
size_t passlen = strlen(pass);
|
||||
int ret;
|
||||
if (passlen >= pw_max) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
ret = passlen + 1;
|
||||
memcpy(pw, pass, ret);
|
||||
out:
|
||||
memset(pass, 0, passlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sockopt_cb(xmpp_conn_t *conn, void *socket)
|
||||
{
|
||||
int timeout = KA_TIMEOUT;
|
||||
int interval = KA_INTERVAL;
|
||||
int count = KA_COUNT;
|
||||
unsigned int user_timeout = USER_TIMEOUT;
|
||||
int ret;
|
||||
int optval = (timeout && interval) ? 1 : 0;
|
||||
|
||||
(void)conn;
|
||||
|
||||
#ifdef _WIN32
|
||||
(void)count;
|
||||
(void)user_timeout;
|
||||
|
||||
SOCKET sock = *((SOCKET *)socket);
|
||||
struct tcp_keepalive ka;
|
||||
DWORD dw = 0;
|
||||
|
||||
ka.onoff = optval;
|
||||
ka.keepalivetime = timeout * 1000;
|
||||
ka.keepaliveinterval = interval * 1000;
|
||||
ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
|
||||
NULL, NULL);
|
||||
#else
|
||||
int sock = *((int *)socket);
|
||||
|
||||
fprintf(stderr, "DEBUG: setting socket options\n");
|
||||
|
||||
ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (optval) {
|
||||
#ifdef TCP_KEEPIDLE
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
|
||||
sizeof(timeout));
|
||||
#elif defined(TCP_KEEPALIVE)
|
||||
/* QNX receives `struct timeval' as argument, but it seems OSX does int
|
||||
*/
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
|
||||
sizeof(timeout));
|
||||
#endif /* TCP_KEEPIDLE */
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef TCP_KEEPINTVL
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
|
||||
sizeof(interval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif /* TCP_KEEPINTVL */
|
||||
}
|
||||
|
||||
if (count) {
|
||||
#ifdef TCP_KEEPCNT
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif /* TCP_KEEPCNT */
|
||||
}
|
||||
|
||||
if (user_timeout) {
|
||||
#ifdef TCP_USER_TIMEOUT
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
|
||||
sizeof(user_timeout));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#elif defined(TCP_RXT_CONNDROPTIME)
|
||||
int rxt = user_timeout / 1000;
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
|
||||
sizeof(rxt));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif /* TCP_USER_TIMEOUT */
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usage(int exit_code)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: complex [options] [<host> [<port>]]\n\n"
|
||||
"Options:\n"
|
||||
" --jid <jid> The JID to use to authenticate.\n"
|
||||
" --pass <pass> The password of the JID.\n"
|
||||
" --tls-cert <cert> Path to client certificate.\n"
|
||||
" --tls-key <key> Path to private key or P12 file.\n\n"
|
||||
" --capath <path> Path to an additional CA trust store "
|
||||
"(directory).\n"
|
||||
" --cafile <path> Path to an additional CA trust store "
|
||||
"(single file).\n"
|
||||
" --disable-tls Disable TLS.\n"
|
||||
" --mandatory-tls Deny plaintext connection.\n"
|
||||
" --trust-tls Trust TLS certificate.\n"
|
||||
" --enable-certfail Enable certfail handler.\n"
|
||||
" --legacy-ssl Use old style SSL.\n"
|
||||
" --legacy-auth Allow legacy authentication.\n"
|
||||
" --zlib Enable compression via zlib.\n"
|
||||
" --dont-reset When using zlib, don't do a full-flush "
|
||||
"after compression.\n"
|
||||
" --verbose Increase the verbosity level.\n"
|
||||
" --tcp-keepalive Configure TCP keepalive.\n\n"
|
||||
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||
"--legacy-ssl\n");
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
xmpp_log_t *log;
|
||||
char *jid = NULL, *password = NULL, *cert = NULL, *key = NULL, *host = NULL,
|
||||
*capath = NULL, *cafile = NULL;
|
||||
long flags = 0;
|
||||
int tcp_keepalive = 0, verbosity = 0, certfail = 0;
|
||||
int i;
|
||||
unsigned long port = 0;
|
||||
|
||||
/* take a jid and password on the command line */
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--help") == 0)
|
||||
usage(0);
|
||||
else if (strcmp(argv[i], "--disable-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||
else if (strcmp(argv[i], "--mandatory-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
|
||||
else if (strcmp(argv[i], "--trust-tls") == 0)
|
||||
flags |= XMPP_CONN_FLAG_TRUST_TLS;
|
||||
else if (strcmp(argv[i], "--legacy-ssl") == 0)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
|
||||
else if (strcmp(argv[i], "--legacy-auth") == 0)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||
else if (strcmp(argv[i], "--zlib") == 0)
|
||||
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
|
||||
else if (strcmp(argv[i], "--dont-reset") == 0)
|
||||
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
|
||||
else if (strcmp(argv[i], "--verbose") == 0)
|
||||
verbosity++;
|
||||
else if (strcmp(argv[i], "--tcp-keepalive") == 0)
|
||||
tcp_keepalive = 1;
|
||||
else if (strcmp(argv[i], "--enable-certfail") == 0)
|
||||
certfail = 1;
|
||||
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
|
||||
jid = argv[i];
|
||||
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
|
||||
password = argv[i];
|
||||
else if ((strcmp(argv[i], "--tls-cert") == 0) && (++i < argc))
|
||||
cert = argv[i];
|
||||
else if ((strcmp(argv[i], "--tls-key") == 0) && (++i < argc))
|
||||
key = argv[i];
|
||||
else if ((strcmp(argv[i], "--capath") == 0) && (++i < argc))
|
||||
capath = argv[i];
|
||||
else if ((strcmp(argv[i], "--cafile") == 0) && (++i < argc))
|
||||
cafile = argv[i];
|
||||
else
|
||||
break;
|
||||
}
|
||||
if ((!jid && !key) || (argc - i) > 2) {
|
||||
usage(1);
|
||||
}
|
||||
|
||||
if (i < argc)
|
||||
host = argv[i];
|
||||
if (i + 1 < argc)
|
||||
port = strtoul(argv[i + 1], NULL, 0);
|
||||
|
||||
/*
|
||||
* Note, this example doesn't handle errors. Applications should check
|
||||
* return values of non-void functions.
|
||||
*/
|
||||
|
||||
/* init library */
|
||||
xmpp_initialize();
|
||||
|
||||
/* pass NULL instead to silence output */
|
||||
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||
/* create a context */
|
||||
ctx = xmpp_ctx_new(NULL, log);
|
||||
xmpp_ctx_set_verbosity(ctx, verbosity);
|
||||
|
||||
/* create a connection */
|
||||
conn = xmpp_conn_new(ctx);
|
||||
|
||||
/* configure connection properties (optional) */
|
||||
xmpp_conn_set_flags(conn, flags);
|
||||
/* configure TCP keepalive (optional) */
|
||||
if (tcp_keepalive)
|
||||
xmpp_conn_set_sockopt_callback(conn, sockopt_cb);
|
||||
|
||||
/* ask for a password if key is protected */
|
||||
xmpp_conn_set_password_callback(conn, password_callback, NULL);
|
||||
/* try at max 3 times in case the user enters the password wrong */
|
||||
xmpp_conn_set_password_retries(conn, 3);
|
||||
/* setup authentication information */
|
||||
if (key) {
|
||||
xmpp_conn_set_client_cert(conn, cert, key);
|
||||
}
|
||||
if (jid)
|
||||
xmpp_conn_set_jid(conn, jid);
|
||||
if (password)
|
||||
xmpp_conn_set_pass(conn, password);
|
||||
|
||||
if (certfail)
|
||||
xmpp_conn_set_certfail_handler(conn, certfail_handler);
|
||||
if (capath)
|
||||
xmpp_conn_set_capath(conn, capath);
|
||||
if (cafile)
|
||||
xmpp_conn_set_cafile(conn, cafile);
|
||||
|
||||
/* initiate connection */
|
||||
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
|
||||
|
||||
/* enter the event loop -
|
||||
our connect handler will trigger an exit */
|
||||
xmpp_run(ctx);
|
||||
}
|
||||
|
||||
/* release our connection and context */
|
||||
xmpp_conn_release(conn);
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
/* final shutdown of the library */
|
||||
xmpp_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* component.c
|
||||
** libstrophe XMPP client library -- external component (XEP-0114) example
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* This example demonstrates simple connection to a server
|
||||
@@ -20,11 +21,11 @@
|
||||
#include <strophe.h>
|
||||
|
||||
/* define a handler for connection events */
|
||||
void conn_handler(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t status,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata)
|
||||
void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
|
||||
|
||||
190
examples/perf.c
Normal file
190
examples/perf.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* perf.c
|
||||
* strophe XMPP client library -- performance measure
|
||||
*
|
||||
* Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||
*
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* performance measure
|
||||
*
|
||||
* Timing code shamelessly borrowed from libtomcrypt/demos/timing.c
|
||||
*/
|
||||
|
||||
#include <strophe.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
|
||||
static void init_timer(void);
|
||||
static void t_start(void);
|
||||
static uint64_t t_read(void);
|
||||
static const char *perf_time_unit;
|
||||
|
||||
#define NUM_SAMPLES 1000u
|
||||
|
||||
static void perf_rand(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_rand_t *rng = xmpp_rand_new(ctx);
|
||||
|
||||
uint64_t t1, t2, t3 = 0;
|
||||
unsigned int n;
|
||||
const size_t alloc_sz = 0x1000u;
|
||||
size_t sz;
|
||||
unsigned char *buf = malloc(alloc_sz);
|
||||
|
||||
/* pre-heat */
|
||||
for (n = 1; n < 4; ++n) {
|
||||
xmpp_rand_bytes(rng, buf, alloc_sz / n);
|
||||
}
|
||||
|
||||
for (sz = 2; sz <= alloc_sz; sz <<= 1) {
|
||||
t2 = 0;
|
||||
for (n = 0; n < NUM_SAMPLES; ++n) {
|
||||
|
||||
t_start();
|
||||
t1 = t_read();
|
||||
|
||||
xmpp_rand_bytes(rng, buf, sz);
|
||||
|
||||
t1 = t_read() - t1;
|
||||
t2 += t1;
|
||||
}
|
||||
t2 /= NUM_SAMPLES;
|
||||
fprintf(stderr, "Reading %6zu bytes from PRNG took %8" PRIu64 " %s\n",
|
||||
sz, t2, perf_time_unit);
|
||||
if (t3) {
|
||||
double d3 = (double)t3, d2 = (double)t2;
|
||||
fprintf(stderr, " +%.2f%%\n",
|
||||
(d2 - d3) / ((d2 + d3) * 0.5) * 100.);
|
||||
}
|
||||
t3 = t2;
|
||||
}
|
||||
free(buf);
|
||||
xmpp_rand_free(ctx, rng);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
/* pass NULL instead to silence output */
|
||||
xmpp_log_t *log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||
/* create a context */
|
||||
xmpp_ctx_t *ctx = xmpp_ctx_new(NULL, log);
|
||||
|
||||
init_timer();
|
||||
|
||||
perf_rand(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMES 100000
|
||||
static uint64_t timer, skew = 0;
|
||||
|
||||
/* RDTSC from Scott Duplichan */
|
||||
static uint64_t rdtsc(void)
|
||||
{
|
||||
#if defined __GNUC__
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
/* version from http://www.mcs.anl.gov/~kazutomo/rdtsc.html
|
||||
* the old code always got a warning issued by gcc, clang did not
|
||||
* complain...
|
||||
*/
|
||||
unsigned hi, lo;
|
||||
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
||||
return ((uint64_t)lo) | (((uint64_t)hi) << 32);
|
||||
#elif defined(__POWERPC__)
|
||||
unsigned long a, b;
|
||||
__asm__ __volatile__("mftbu %1 \nmftb %0\n" : "=r"(a), "=r"(b));
|
||||
return (((uint64_t)b) << 32ULL) | ((uint64_t)a);
|
||||
#elif defined(__ia64__) /* gcc-IA64 version */
|
||||
unsigned long result;
|
||||
__asm__ __volatile__("mov %0=ar.itc" : "=r"(result)::"memory");
|
||||
while (__builtin_expect((int)result == -1, 0))
|
||||
__asm__ __volatile__("mov %0=ar.itc" : "=r"(result)::"memory");
|
||||
return result;
|
||||
#elif defined(__sparc__)
|
||||
#if defined(__arch64__)
|
||||
uint64_t a;
|
||||
asm volatile("rd %%tick,%0" : "=r"(a));
|
||||
return a;
|
||||
#else
|
||||
register unsigned long x, y;
|
||||
__asm__ __volatile__("rd %%tick, %0; clruw %0, %1; srlx %0, 32, %0"
|
||||
: "=r"(x), "=r"(y)
|
||||
: "0"(x), "1"(y));
|
||||
return ((unsigned long long)x << 32) | y;
|
||||
#endif
|
||||
#elif defined(__aarch64__)
|
||||
uint64_t CNTVCT_EL0;
|
||||
__asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(CNTVCT_EL0));
|
||||
return CNTVCT_EL0;
|
||||
#elif defined HAVE_CLOCK_GETTIME
|
||||
#define USE_CLOCK_GETTIME
|
||||
struct timespec result;
|
||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &result);
|
||||
return result.tv_sec * 1000000000 + result.tv_nsec;
|
||||
#else
|
||||
return clock();
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
/* Microsoft and Intel Windows compilers */
|
||||
#elif defined _M_IX86
|
||||
__asm rdtsc
|
||||
#elif defined _M_AMD64
|
||||
return __rdtsc();
|
||||
#elif defined _M_IA64
|
||||
#if defined __INTEL_COMPILER
|
||||
#include <ia64intrin.h>
|
||||
#endif
|
||||
return __getReg(3116);
|
||||
#elif defined HAVE_CLOCK_GETTIME
|
||||
#define USE_CLOCK_GETTIME
|
||||
struct timespec result;
|
||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &result);
|
||||
return result.tv_sec * 1000000000 + result.tv_nsec;
|
||||
#else
|
||||
return clock();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined USE_CLOCK_GETTIME
|
||||
static const char *perf_time_unit = "ns";
|
||||
#else
|
||||
static const char *perf_time_unit = "cycles";
|
||||
#endif
|
||||
|
||||
static void t_start(void)
|
||||
{
|
||||
timer = rdtsc();
|
||||
}
|
||||
|
||||
static uint64_t t_read(void)
|
||||
{
|
||||
return rdtsc() - timer;
|
||||
}
|
||||
|
||||
static void init_timer(void)
|
||||
{
|
||||
uint64_t c1, c2, t1, t2;
|
||||
unsigned long y1;
|
||||
|
||||
c1 = c2 = (uint64_t)-1;
|
||||
for (y1 = 0; y1 < TIMES * 100; y1++) {
|
||||
t_start();
|
||||
t1 = t_read();
|
||||
t2 = (t_read() - t1) >> 1;
|
||||
|
||||
c1 = (t1 > c1) ? t1 : c1;
|
||||
c2 = (t2 > c2) ? t2 : c2;
|
||||
}
|
||||
skew = c2 - c1;
|
||||
fprintf(stderr, "Clock Skew: %lu\n", (unsigned long)skew);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* register.c
|
||||
* strophe XMPP client library -- In-band registration (XEP-0077)
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -102,9 +103,7 @@ iq_reg_send_form(xmpp_reg_t *reg, xmpp_conn_t *conn, xmpp_stanza_t *stanza)
|
||||
}
|
||||
}
|
||||
|
||||
static int iq_reg2_cb(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int iq_reg2_cb(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
const char *type;
|
||||
|
||||
@@ -129,9 +128,7 @@ quit:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iq_reg_cb(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int iq_reg_cb(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
|
||||
xmpp_stanza_t *registered = NULL;
|
||||
@@ -166,9 +163,8 @@ quit:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_error(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int
|
||||
_handle_error(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
(void)stanza;
|
||||
(void)userdata;
|
||||
@@ -179,9 +175,9 @@ static int _handle_error(xmpp_conn_t *const conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_proceedtls_default(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int _handle_proceedtls_default(xmpp_conn_t *conn,
|
||||
xmpp_stanza_t *stanza,
|
||||
void *userdata)
|
||||
{
|
||||
const char *name = xmpp_stanza_get_name(stanza);
|
||||
|
||||
@@ -201,8 +197,7 @@ static int _handle_proceedtls_default(xmpp_conn_t *const conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_missing_features(xmpp_conn_t *const conn,
|
||||
void *const userdata)
|
||||
static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
|
||||
@@ -212,9 +207,8 @@ static int _handle_missing_features(xmpp_conn_t *const conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_features(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int
|
||||
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
|
||||
xmpp_ctx_t *ctx = reg->ctx;
|
||||
@@ -265,11 +259,11 @@ static int _handle_features(xmpp_conn_t *const conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void conn_handler(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t status,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata)
|
||||
static void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
|
||||
int secured;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* roster.c
|
||||
** libstrophe XMPP client library -- handler example
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* This example demonstrates basic handler functions by printing out
|
||||
@@ -18,9 +19,7 @@
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
int handle_reply(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
int handle_reply(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
xmpp_stanza_t *query, *item;
|
||||
const char *type, *name;
|
||||
@@ -51,11 +50,11 @@ int handle_reply(xmpp_conn_t *const conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_handler(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t status,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata)
|
||||
void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
xmpp_stanza_t *iq, *query;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* vcard.c
|
||||
* strophe XMPP client library -- vCard example
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
@@ -160,7 +161,7 @@ exit:
|
||||
return cb;
|
||||
}
|
||||
|
||||
static int timedout(xmpp_conn_t *const conn, void *const userdata)
|
||||
static int timedout(xmpp_conn_t *conn, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
|
||||
@@ -170,9 +171,7 @@ static int timedout(xmpp_conn_t *const conn, void *const userdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recv_vcard(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata)
|
||||
static int recv_vcard(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
vcard_t *vc = userdata;
|
||||
vcard_cb_t cb;
|
||||
@@ -217,11 +216,11 @@ static void send_vcard_req(xmpp_conn_t *conn, const char *to, const char *id)
|
||||
xmpp_conn_get_bound_jid(conn), to, id);
|
||||
}
|
||||
|
||||
static void conn_handler(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t status,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata)
|
||||
static void conn_handler(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t status,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata)
|
||||
{
|
||||
vcard_t *vc = userdata;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ includedir=@includedir@
|
||||
|
||||
Name: libstrophe
|
||||
Description: A simple, lightweight C library for writing XMPP clients
|
||||
URL: http://strophe.im/libstrophe/
|
||||
URL: https://strophe.im/libstrophe/
|
||||
Version: @VERSION@
|
||||
Requires:
|
||||
Requires.private: @PC_REQUIRES@
|
||||
|
||||
28
m4/ac_try_compile2.m4
Normal file
28
m4/ac_try_compile2.m4
Normal file
@@ -0,0 +1,28 @@
|
||||
dnl Like AC_TRY_EVAL but also errors out if the compiler generates
|
||||
dnl _any_ output. Some compilers might issue warnings which we want
|
||||
dnl to catch.
|
||||
AC_DEFUN([AC_TRY_EVAL2],
|
||||
[{ (eval echo configure:__oline__: \"[$]$1\") 1>&AS_MESSAGE_LOG_FD; dnl
|
||||
(eval [$]$1) 2>&AS_MESSAGE_LOG_FD; _out=`eval [$]$1 2>&1` && test "x$_out" = x; }])
|
||||
|
||||
dnl Like AC_TRY_COMPILE but calls AC_TRY_EVAL2 instead of AC_TRY_EVAL
|
||||
AC_DEFUN([AC_TRY_COMPILE2],
|
||||
[cat > conftest.$ac_ext <<EOF
|
||||
[#]line __oline__ "configure"
|
||||
#include "confdefs.h"
|
||||
[$1]
|
||||
int main(void) {
|
||||
[$2]
|
||||
; return 0; }
|
||||
EOF
|
||||
if AC_TRY_EVAL2(ac_compile); then
|
||||
ifelse([$3], , :, [rm -rf conftest*
|
||||
$3])
|
||||
else
|
||||
echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD
|
||||
cat conftest.$ac_ext >&AS_MESSAGE_LOG_FD
|
||||
ifelse([$4], , , [ rm -rf conftest*
|
||||
$4
|
||||
])dnl
|
||||
fi
|
||||
rm -f conftest*])
|
||||
239
m4/ax_valgrind_check.m4
Normal file
239
m4/ax_valgrind_check.m4
Normal file
@@ -0,0 +1,239 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off)
|
||||
# AX_VALGRIND_CHECK()
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows
|
||||
# running `make check` under a variety of Valgrind tools to check for
|
||||
# memory and threading errors.
|
||||
#
|
||||
# Defines VALGRIND_CHECK_RULES which should be substituted in your
|
||||
# Makefile; and $enable_valgrind which can be used in subsequent configure
|
||||
# output. VALGRIND_ENABLED is defined and substituted, and corresponds to
|
||||
# the value of the --enable-valgrind option, which defaults to being
|
||||
# enabled if Valgrind is installed and disabled otherwise. Individual
|
||||
# Valgrind tools can be disabled via --disable-valgrind-<tool>, the
|
||||
# default is configurable via the AX_VALGRIND_DFLT command or is to use
|
||||
# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT
|
||||
# calls must be made before the call to AX_VALGRIND_CHECK.
|
||||
#
|
||||
# If unit tests are written using a shell script and automake's
|
||||
# LOG_COMPILER system, the $(VALGRIND) variable can be used within the
|
||||
# shell scripts to enable Valgrind, as described here:
|
||||
#
|
||||
# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html
|
||||
#
|
||||
# Usage example:
|
||||
#
|
||||
# configure.ac:
|
||||
#
|
||||
# AX_VALGRIND_DFLT([sgcheck], [off])
|
||||
# AX_VALGRIND_CHECK
|
||||
#
|
||||
# in each Makefile.am with tests:
|
||||
#
|
||||
# @VALGRIND_CHECK_RULES@
|
||||
# VALGRIND_SUPPRESSIONS_FILES = my-project.supp
|
||||
# EXTRA_DIST = my-project.supp
|
||||
#
|
||||
# This results in a "check-valgrind" rule being added. Running `make
|
||||
# check-valgrind` in that directory will recursively run the module's test
|
||||
# suite (`make check`) once for each of the available Valgrind tools (out
|
||||
# of memcheck, helgrind and drd) while the sgcheck will be skipped unless
|
||||
# enabled again on the commandline with --enable-valgrind-sgcheck. The
|
||||
# results for each check will be output to test-suite-$toolname.log. The
|
||||
# target will succeed if there are zero errors and fail otherwise.
|
||||
#
|
||||
# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in
|
||||
# memcheck, helgrind, drd and sgcheck. These are useful because often only
|
||||
# some of those tools can be ran cleanly on a codebase.
|
||||
#
|
||||
# The macro supports running with and without libtool.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014, 2015, 2016 Philip Withnall <philip.withnall@collabora.co.uk>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 23
|
||||
|
||||
dnl Configured tools
|
||||
m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]])
|
||||
m4_set_add_all([valgrind_exp_tool_set], [sgcheck])
|
||||
m4_foreach([vgtool], [valgrind_tool_list],
|
||||
[m4_define([en_dflt_valgrind_]vgtool, [on])])
|
||||
|
||||
AC_DEFUN([AX_VALGRIND_DFLT],[
|
||||
m4_define([en_dflt_valgrind_$1], [$2])
|
||||
])dnl
|
||||
|
||||
AC_DEFUN([AX_VALGRIND_CHECK],[
|
||||
AM_EXTRA_RECURSIVE_TARGETS([check-valgrind])
|
||||
m4_foreach([vgtool], [valgrind_tool_list],
|
||||
[AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)])
|
||||
|
||||
dnl Check for --enable-valgrind
|
||||
AC_ARG_ENABLE([valgrind],
|
||||
[AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])],
|
||||
[enable_valgrind=$enableval],[enable_valgrind=])
|
||||
|
||||
AS_IF([test "$enable_valgrind" != "no"],[
|
||||
# Check for Valgrind.
|
||||
AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind])
|
||||
AS_IF([test "$VALGRIND" = ""],[
|
||||
AS_IF([test "$enable_valgrind" = "yes"],[
|
||||
AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind])
|
||||
],[
|
||||
enable_valgrind=no
|
||||
])
|
||||
],[
|
||||
enable_valgrind=yes
|
||||
])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
|
||||
AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind])
|
||||
|
||||
# Check for Valgrind tools we care about.
|
||||
[valgrind_enabled_tools=]
|
||||
m4_foreach([vgtool],[valgrind_tool_list],[
|
||||
AC_ARG_ENABLE([valgrind-]vgtool,
|
||||
m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl
|
||||
[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl
|
||||
[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]),
|
||||
[enable_valgrind_]vgtool[=$enableval],
|
||||
[enable_valgrind_]vgtool[=])
|
||||
AS_IF([test "$enable_valgrind" = "no"],[
|
||||
enable_valgrind_]vgtool[=no],
|
||||
[test "$enable_valgrind_]vgtool[" ]dnl
|
||||
m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[
|
||||
AC_CACHE_CHECK([for Valgrind tool ]vgtool,
|
||||
[ax_cv_valgrind_tool_]vgtool,[
|
||||
ax_cv_valgrind_tool_]vgtool[=no
|
||||
m4_set_contains([valgrind_exp_tool_set],vgtool,
|
||||
[m4_define([vgtoolx],[exp-]vgtool)],
|
||||
[m4_define([vgtoolx],vgtool)])
|
||||
AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[
|
||||
ax_cv_valgrind_tool_]vgtool[=yes
|
||||
])
|
||||
])
|
||||
AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[
|
||||
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
|
||||
AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool)
|
||||
],[
|
||||
enable_valgrind_]vgtool[=no
|
||||
])
|
||||
],[
|
||||
enable_valgrind_]vgtool[=yes
|
||||
])
|
||||
])
|
||||
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
|
||||
valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])["
|
||||
])
|
||||
AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool)
|
||||
])
|
||||
AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["])
|
||||
AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools])
|
||||
|
||||
[VALGRIND_CHECK_RULES='
|
||||
# Valgrind check
|
||||
#
|
||||
# Optional:
|
||||
# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions
|
||||
# files to load. (Default: empty)
|
||||
# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools.
|
||||
# (Default: --num-callers=30)
|
||||
# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of:
|
||||
# memcheck, helgrind, drd, sgcheck). (Default: various)
|
||||
|
||||
# Optional variables
|
||||
VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES))
|
||||
VALGRIND_FLAGS ?= --num-callers=30
|
||||
VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no
|
||||
VALGRIND_helgrind_FLAGS ?= --history-level=approx
|
||||
VALGRIND_drd_FLAGS ?=
|
||||
VALGRIND_sgcheck_FLAGS ?=
|
||||
|
||||
# Internal use
|
||||
valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools)))
|
||||
|
||||
valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS)
|
||||
valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS)
|
||||
valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS)
|
||||
valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS)
|
||||
|
||||
valgrind_quiet = $(valgrind_quiet_$(V))
|
||||
valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY))
|
||||
valgrind_quiet_0 = --quiet
|
||||
valgrind_v_use = $(valgrind_v_use_$(V))
|
||||
valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY))
|
||||
valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-local,%,$''@):;
|
||||
|
||||
# Support running with and without libtool.
|
||||
ifneq ($(LIBTOOL),)
|
||||
valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute
|
||||
else
|
||||
valgrind_lt =
|
||||
endif
|
||||
|
||||
# Use recursive makes in order to ignore errors during check
|
||||
check-valgrind-local:
|
||||
ifeq ($(VALGRIND_ENABLED),yes)
|
||||
$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \
|
||||
$(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool))
|
||||
else
|
||||
@echo "Need to reconfigure with --enable-valgrind"
|
||||
endif
|
||||
|
||||
# Valgrind running
|
||||
VALGRIND_TESTS_ENVIRONMENT = \
|
||||
$(TESTS_ENVIRONMENT) \
|
||||
env VALGRIND=$(VALGRIND) \
|
||||
G_SLICE=always-malloc,debug-blocks \
|
||||
G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly
|
||||
|
||||
VALGRIND_LOG_COMPILER = \
|
||||
$(valgrind_lt) \
|
||||
$(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS)
|
||||
|
||||
define valgrind_tool_rule
|
||||
check-valgrind-$(1)-local:
|
||||
ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes)
|
||||
ifneq ($$(TESTS),)
|
||||
$$(valgrind_v_use)$$(MAKE) check-TESTS \
|
||||
TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \
|
||||
LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \
|
||||
LOG_FLAGS="$$(valgrind_$(1)_flags)" \
|
||||
TEST_SUITE_LOG=test-suite-$(1).log
|
||||
endif
|
||||
else ifeq ($$(VALGRIND_ENABLED),yes)
|
||||
@echo "Need to reconfigure with --enable-valgrind-$(1)"
|
||||
else
|
||||
@echo "Need to reconfigure with --enable-valgrind"
|
||||
endif
|
||||
endef
|
||||
|
||||
$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool))))
|
||||
|
||||
A''M_DISTCHECK_CONFIGURE_FLAGS ?=
|
||||
A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind
|
||||
|
||||
MOSTLYCLEANFILES ?=
|
||||
MOSTLYCLEANFILES += $(valgrind_log_files)
|
||||
|
||||
.PHONY: check-valgrind $(addprefix check-valgrind-,$(valgrind_tools))
|
||||
']
|
||||
|
||||
AC_SUBST([VALGRIND_CHECK_RULES])
|
||||
m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])])
|
||||
])
|
||||
1260
src/auth.c
1260
src/auth.c
File diff suppressed because it is too large
Load Diff
246
src/common.h
246
src/common.h
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* common.h
|
||||
** strophe XMPP client library -- internal common structures
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -26,16 +27,20 @@
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
#include "parser.h"
|
||||
#include "rand.h"
|
||||
#include "snprintf.h"
|
||||
|
||||
/** handlers **/
|
||||
#if (__STDC_VERSION__ >= 202000L)
|
||||
typedef void* xmpp_void_handler;
|
||||
#else
|
||||
typedef int (*xmpp_void_handler)();
|
||||
#endif
|
||||
|
||||
typedef struct _xmpp_handlist_t xmpp_handlist_t;
|
||||
struct _xmpp_handlist_t {
|
||||
/* common members */
|
||||
int user_handler;
|
||||
int (*handler)();
|
||||
xmpp_void_handler handler;
|
||||
void *userdata;
|
||||
int enabled; /* handlers are added disabled and enabled after the
|
||||
* handler chain is processed to prevent stanzas from
|
||||
@@ -77,6 +82,7 @@ typedef struct _xmpp_connlist_t {
|
||||
struct _xmpp_ctx_t {
|
||||
const xmpp_mem_t *mem;
|
||||
const xmpp_log_t *log;
|
||||
int verbosity;
|
||||
|
||||
xmpp_rand_t *rand;
|
||||
xmpp_loop_status_t loop_status;
|
||||
@@ -87,33 +93,50 @@ struct _xmpp_ctx_t {
|
||||
};
|
||||
|
||||
/* convenience functions for accessing the context */
|
||||
void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size);
|
||||
void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size);
|
||||
char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s);
|
||||
|
||||
void xmpp_log(const xmpp_ctx_t *const ctx,
|
||||
const xmpp_log_level_t level,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
va_list ap);
|
||||
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size);
|
||||
void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size);
|
||||
char *strophe_strdup(const xmpp_ctx_t *ctx, const char *s);
|
||||
char *strophe_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len);
|
||||
void strophe_free(const xmpp_ctx_t *ctx, void *p);
|
||||
#define strophe_free_and_null(ctx, p) \
|
||||
do { \
|
||||
if (p) { \
|
||||
strophe_free(ctx, (p)); \
|
||||
(p) = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* wrappers for xmpp_log at specific levels */
|
||||
void xmpp_error(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...);
|
||||
void xmpp_warn(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...);
|
||||
void xmpp_info(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...);
|
||||
void xmpp_debug(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...);
|
||||
void strophe_error(const xmpp_ctx_t *ctx,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
...);
|
||||
void strophe_warn(const xmpp_ctx_t *ctx,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
...);
|
||||
void strophe_info(const xmpp_ctx_t *ctx,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
...);
|
||||
void strophe_debug(const xmpp_ctx_t *ctx,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
...);
|
||||
void strophe_debug_verbose(
|
||||
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||
|
||||
void strophe_log_internal(const xmpp_ctx_t *ctx,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
va_list ap);
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
#define STR_MAYBE_NULL(p) (p) ? (p) : "(null)"
|
||||
#else
|
||||
#define STR_MAYBE_NULL(p) (p)
|
||||
#endif
|
||||
|
||||
/** connection **/
|
||||
|
||||
@@ -124,13 +147,24 @@ typedef enum {
|
||||
XMPP_STATE_CONNECTED
|
||||
} xmpp_conn_state_t;
|
||||
|
||||
typedef enum {
|
||||
XMPP_QUEUE_STROPHE = 0x1,
|
||||
XMPP_QUEUE_USER = 0x2,
|
||||
XMPP_QUEUE_SM = 0x800,
|
||||
XMPP_QUEUE_SM_STROPHE = XMPP_QUEUE_SM | XMPP_QUEUE_STROPHE,
|
||||
} xmpp_send_queue_owner_t;
|
||||
|
||||
typedef struct _xmpp_send_queue_t xmpp_send_queue_t;
|
||||
struct _xmpp_send_queue_t {
|
||||
char *data;
|
||||
size_t len;
|
||||
size_t written;
|
||||
int wip;
|
||||
xmpp_send_queue_owner_t owner;
|
||||
void *userdata;
|
||||
uint32_t sm_h;
|
||||
|
||||
xmpp_send_queue_t *next;
|
||||
xmpp_send_queue_t *prev, *next;
|
||||
};
|
||||
|
||||
#define UNUSED(x) ((void)(x))
|
||||
@@ -143,9 +177,17 @@ struct _xmpp_send_queue_t {
|
||||
#define SASL_MASK_SCRAMSHA1 (1 << 3)
|
||||
#define SASL_MASK_SCRAMSHA256 (1 << 4)
|
||||
#define SASL_MASK_SCRAMSHA512 (1 << 5)
|
||||
#define SASL_MASK_EXTERNAL (1 << 6)
|
||||
#define SASL_MASK_SCRAMSHA1_PLUS (1 << 7)
|
||||
#define SASL_MASK_SCRAMSHA256_PLUS (1 << 8)
|
||||
#define SASL_MASK_SCRAMSHA512_PLUS (1 << 9)
|
||||
|
||||
#define SASL_MASK_SCRAM \
|
||||
#define SASL_MASK_SCRAM_PLUS \
|
||||
(SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_PLUS | \
|
||||
SASL_MASK_SCRAMSHA512_PLUS)
|
||||
#define SASL_MASK_SCRAM_WEAK \
|
||||
(SASL_MASK_SCRAMSHA1 | SASL_MASK_SCRAMSHA256 | SASL_MASK_SCRAMSHA512)
|
||||
#define SASL_MASK_SCRAM (SASL_MASK_SCRAM_PLUS | SASL_MASK_SCRAM_WEAK)
|
||||
|
||||
enum {
|
||||
XMPP_PORT_CLIENT = 5222,
|
||||
@@ -153,9 +195,47 @@ enum {
|
||||
XMPP_PORT_COMPONENT = 5347,
|
||||
};
|
||||
|
||||
typedef void (*xmpp_open_handler)(xmpp_conn_t *const conn);
|
||||
typedef void (*xmpp_open_handler)(xmpp_conn_t *conn);
|
||||
|
||||
typedef struct {
|
||||
xmpp_send_queue_t *head, *tail;
|
||||
} xmpp_queue_t;
|
||||
|
||||
struct _xmpp_sm_t {
|
||||
xmpp_ctx_t *ctx;
|
||||
int sm_support;
|
||||
int sm_enabled;
|
||||
int can_resume, resume, dont_request_resume;
|
||||
xmpp_queue_t sm_queue;
|
||||
int r_sent;
|
||||
uint32_t sm_handled_nr;
|
||||
uint32_t sm_sent_nr;
|
||||
char *id, *previd, *bound_jid;
|
||||
xmpp_stanza_t *bind;
|
||||
};
|
||||
|
||||
struct conn_interface {
|
||||
int (*read)(struct conn_interface *intf, void *buff, size_t len);
|
||||
int (*write)(struct conn_interface *intf, const void *buff, size_t len);
|
||||
int (*flush)(struct conn_interface *intf);
|
||||
int (*pending)(struct conn_interface *intf);
|
||||
int (*get_error)(struct conn_interface *intf);
|
||||
int (*error_is_recoverable)(struct conn_interface *intf, int err);
|
||||
xmpp_conn_t *conn;
|
||||
};
|
||||
|
||||
int conn_interface_write(struct conn_interface *intf,
|
||||
const void *buff,
|
||||
size_t len);
|
||||
int conn_int_nop(struct conn_interface *intf);
|
||||
|
||||
int compression_init(xmpp_conn_t *conn);
|
||||
void compression_free(xmpp_conn_t *conn);
|
||||
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text);
|
||||
|
||||
struct _xmpp_conn_t {
|
||||
struct conn_interface intf;
|
||||
|
||||
unsigned int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_type_t type;
|
||||
@@ -166,26 +246,47 @@ struct _xmpp_conn_t {
|
||||
int error;
|
||||
xmpp_stream_error_t *stream_error;
|
||||
|
||||
xmpp_sock_t *xsock;
|
||||
sock_t sock;
|
||||
int ka_timeout; /* TCP keepalive timeout */
|
||||
int ka_interval; /* TCP keepalive interval */
|
||||
int ka_count; /* TCP keepalive count */
|
||||
|
||||
tls_t *tls;
|
||||
char *tls_cafile;
|
||||
int tls_support;
|
||||
int tls_disabled;
|
||||
int tls_mandatory;
|
||||
int tls_legacy_ssl;
|
||||
int tls_trust;
|
||||
char *tls_cafile;
|
||||
char *tls_capath;
|
||||
char *tls_client_cert;
|
||||
char *tls_client_key;
|
||||
int tls_failed; /* set when tls fails, so we don't try again */
|
||||
int sasl_support; /* if true, field is a bitfield of supported
|
||||
mechanisms */
|
||||
int auth_legacy_enabled;
|
||||
int secured; /* set when stream is secured with TLS */
|
||||
xmpp_certfail_handler certfail_handler;
|
||||
xmpp_password_callback password_callback;
|
||||
void *password_callback_userdata;
|
||||
struct {
|
||||
char pass[1024];
|
||||
unsigned char fname_hash[XMPP_SHA1_DIGEST_SIZE];
|
||||
size_t passlen, fnamelen;
|
||||
} password_cache;
|
||||
unsigned int password_retries;
|
||||
|
||||
/* if server returns <bind/> or <session/> we must do them */
|
||||
int bind_required;
|
||||
int session_required;
|
||||
int sm_disable;
|
||||
xmpp_sm_state_t *sm_state;
|
||||
|
||||
struct {
|
||||
struct xmpp_compression *state;
|
||||
int allowed, supported, dont_reset;
|
||||
} compression;
|
||||
|
||||
char *lang;
|
||||
char *domain;
|
||||
@@ -198,6 +299,7 @@ struct _xmpp_conn_t {
|
||||
int blocking_send;
|
||||
int send_queue_max;
|
||||
int send_queue_len;
|
||||
int send_queue_user_len;
|
||||
xmpp_send_queue_t *send_queue_head;
|
||||
xmpp_send_queue_t *send_queue_tail;
|
||||
|
||||
@@ -213,8 +315,9 @@ struct _xmpp_conn_t {
|
||||
/* stream open handler */
|
||||
xmpp_open_handler open_handler;
|
||||
|
||||
/* user handlers only get called after authentication */
|
||||
int authenticated;
|
||||
/* user handlers only get called after the stream negotiation has completed
|
||||
*/
|
||||
int stream_negotiation_completed;
|
||||
|
||||
/* connection events handler */
|
||||
xmpp_conn_handler conn_handler;
|
||||
@@ -224,15 +327,18 @@ struct _xmpp_conn_t {
|
||||
xmpp_handlist_t *timed_handlers;
|
||||
hash_t *id_handlers;
|
||||
xmpp_handlist_t *handlers;
|
||||
xmpp_sockopt_callback sockopt_cb;
|
||||
xmpp_sm_callback sm_callback;
|
||||
void *sm_callback_ctx;
|
||||
};
|
||||
|
||||
void conn_disconnect(xmpp_conn_t *const conn);
|
||||
void conn_disconnect_clean(xmpp_conn_t *const conn);
|
||||
void conn_established(xmpp_conn_t *const conn);
|
||||
void conn_open_stream(xmpp_conn_t *const conn);
|
||||
int conn_tls_start(xmpp_conn_t *const conn);
|
||||
void conn_prepare_reset(xmpp_conn_t *const conn, xmpp_open_handler handler);
|
||||
void conn_parser_reset(xmpp_conn_t *const conn);
|
||||
void conn_disconnect(xmpp_conn_t *conn);
|
||||
void conn_disconnect_clean(xmpp_conn_t *conn);
|
||||
void conn_established(xmpp_conn_t *conn);
|
||||
void conn_open_stream(xmpp_conn_t *conn);
|
||||
int conn_tls_start(xmpp_conn_t *conn);
|
||||
void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler);
|
||||
void conn_parser_reset(xmpp_conn_t *conn);
|
||||
|
||||
typedef enum {
|
||||
XMPP_STANZA_UNKNOWN,
|
||||
@@ -257,32 +363,54 @@ struct _xmpp_stanza_t {
|
||||
};
|
||||
|
||||
/* handler management */
|
||||
void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza);
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t *const ctx);
|
||||
void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t *ctx);
|
||||
void handler_reset_timed(xmpp_conn_t *conn, int user_only);
|
||||
void handler_add_timed(xmpp_conn_t *const conn,
|
||||
void handler_add_timed(xmpp_conn_t *conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void *const userdata);
|
||||
void handler_add_id(xmpp_conn_t *const conn,
|
||||
unsigned long period,
|
||||
void *userdata);
|
||||
void handler_add_id(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const id,
|
||||
void *const userdata);
|
||||
void handler_add(xmpp_conn_t *const conn,
|
||||
const char *id,
|
||||
void *userdata);
|
||||
void handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const ns,
|
||||
const char *const name,
|
||||
const char *const type,
|
||||
void *const userdata);
|
||||
const char *ns,
|
||||
const char *name,
|
||||
const char *type,
|
||||
void *userdata);
|
||||
void handler_system_delete_all(xmpp_conn_t *conn);
|
||||
|
||||
/* utility functions */
|
||||
void disconnect_mem_error(xmpp_conn_t *const conn);
|
||||
void trigger_sm_callback(xmpp_conn_t *conn);
|
||||
void reset_sm_state(xmpp_sm_state_t *sm_state);
|
||||
void disconnect_mem_error(xmpp_conn_t *conn);
|
||||
|
||||
/* auth functions */
|
||||
void auth_handle_open(xmpp_conn_t *const conn);
|
||||
void auth_handle_component_open(xmpp_conn_t *const conn);
|
||||
void auth_handle_open_raw(xmpp_conn_t *const conn);
|
||||
void auth_handle_open_stub(xmpp_conn_t *const conn);
|
||||
void auth_handle_open(xmpp_conn_t *conn);
|
||||
void auth_handle_component_open(xmpp_conn_t *conn);
|
||||
void auth_handle_open_raw(xmpp_conn_t *conn);
|
||||
void auth_handle_open_stub(xmpp_conn_t *conn);
|
||||
|
||||
/* queue functions */
|
||||
void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item);
|
||||
xmpp_send_queue_t *peek_queue_front(xmpp_queue_t *queue);
|
||||
xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue);
|
||||
char *queue_element_free(xmpp_ctx_t *ctx, xmpp_send_queue_t *e);
|
||||
|
||||
/* send functions */
|
||||
void send_raw(xmpp_conn_t *conn,
|
||||
const char *data,
|
||||
size_t len,
|
||||
xmpp_send_queue_owner_t owner,
|
||||
void *userdata);
|
||||
/* this is a bit special as it will always mark the sent string as
|
||||
* owned by libstrophe
|
||||
*/
|
||||
void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
|
||||
void send_stanza(xmpp_conn_t *conn,
|
||||
xmpp_stanza_t *stanza,
|
||||
xmpp_send_queue_owner_t owner);
|
||||
|
||||
#endif /* __LIBSTROPHE_COMMON_H__ */
|
||||
|
||||
270
src/compression.c
Normal file
270
src/compression.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* compression.c
|
||||
** strophe XMPP client library -- XEP-0138 Stream Compression
|
||||
**
|
||||
** Copyright (C) 2024 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* XEP-0138 Stream Compression.
|
||||
*/
|
||||
#include <zlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifndef STROPHE_COMPRESSION_BUFFER_SIZE
|
||||
/** Max buffer size for compressed data (send & receive). */
|
||||
#define STROPHE_COMPRESSION_BUFFER_SIZE 4096
|
||||
#endif
|
||||
|
||||
struct zlib_compression {
|
||||
void *buffer, *buffer_end;
|
||||
z_stream stream;
|
||||
};
|
||||
|
||||
struct xmpp_compression {
|
||||
xmpp_conn_t *conn;
|
||||
struct zlib_compression compression, decompression;
|
||||
struct conn_interface next;
|
||||
};
|
||||
|
||||
static int _conn_decompress(struct xmpp_compression *comp,
|
||||
size_t c_len,
|
||||
void *buff,
|
||||
size_t len)
|
||||
{
|
||||
if (comp->decompression.stream.next_in == NULL) {
|
||||
comp->decompression.stream.next_in = comp->decompression.buffer;
|
||||
comp->decompression.buffer_end =
|
||||
comp->decompression.stream.next_in + c_len;
|
||||
comp->decompression.stream.avail_in = c_len;
|
||||
} else if (c_len) {
|
||||
strophe_error(comp->conn->ctx, "zlib",
|
||||
"_conn_decompress() called with c_len=%zu", c_len);
|
||||
}
|
||||
comp->decompression.stream.next_out = buff;
|
||||
comp->decompression.stream.avail_out = len;
|
||||
int ret = inflate(&comp->decompression.stream, Z_SYNC_FLUSH);
|
||||
switch (ret) {
|
||||
case Z_STREAM_END:
|
||||
case Z_OK:
|
||||
if (comp->decompression.buffer_end ==
|
||||
comp->decompression.stream.next_in)
|
||||
comp->decompression.stream.next_in = NULL;
|
||||
return comp->decompression.stream.next_out - (Bytef *)buff;
|
||||
case Z_BUF_ERROR:
|
||||
break;
|
||||
default:
|
||||
strophe_error(comp->conn->ctx, "zlib", "inflate error %d", ret);
|
||||
comp->conn->error = ret;
|
||||
conn_disconnect(comp->conn);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compression_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
xmpp_conn_t *conn = intf->conn;
|
||||
struct xmpp_compression *comp = conn->compression.state;
|
||||
void *dbuff = buff;
|
||||
size_t dlen = len;
|
||||
if (comp->decompression.stream.next_in != NULL) {
|
||||
return _conn_decompress(comp, 0, buff, len);
|
||||
}
|
||||
dbuff = comp->decompression.buffer;
|
||||
dlen = STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||
int ret = comp->next.read(intf, dbuff, dlen);
|
||||
if (ret > 0) {
|
||||
return _conn_decompress(comp, ret, buff, len);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _try_compressed_write_to_network(xmpp_conn_t *conn, int force)
|
||||
{
|
||||
struct xmpp_compression *comp = conn->compression.state;
|
||||
int ret = 0;
|
||||
ptrdiff_t len =
|
||||
comp->compression.stream.next_out - (Bytef *)comp->compression.buffer;
|
||||
int buffer_full =
|
||||
comp->compression.stream.next_out == comp->compression.buffer_end;
|
||||
if ((buffer_full || force) && len > 0) {
|
||||
ret = conn_interface_write(&comp->next, comp->compression.buffer, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
comp->compression.stream.next_out = comp->compression.buffer;
|
||||
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
_compression_write(xmpp_conn_t *conn, const void *buff, size_t len, int flush)
|
||||
{
|
||||
int ret;
|
||||
const void *buff_end = buff + len;
|
||||
struct xmpp_compression *comp = conn->compression.state;
|
||||
comp->compression.stream.next_in = (Bytef *)buff;
|
||||
comp->compression.stream.avail_in = len;
|
||||
do {
|
||||
ret = _try_compressed_write_to_network(conn, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = deflate(&comp->compression.stream, flush);
|
||||
if (ret == Z_STREAM_END) {
|
||||
break;
|
||||
}
|
||||
if (flush && ret == Z_BUF_ERROR) {
|
||||
break;
|
||||
}
|
||||
if (ret != Z_OK) {
|
||||
strophe_error(conn->ctx, "zlib", "deflate error %d", ret);
|
||||
conn->error = ret;
|
||||
conn_disconnect(conn);
|
||||
return ret;
|
||||
}
|
||||
ret = comp->compression.stream.next_in - (Bytef *)buff;
|
||||
} while (comp->compression.stream.next_in < (Bytef *)buff_end);
|
||||
if (flush) {
|
||||
ret = _try_compressed_write_to_network(conn, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
compression_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
return _compression_write(intf->conn, buff, len, Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
static int compression_flush(struct conn_interface *intf)
|
||||
{
|
||||
xmpp_conn_t *conn = intf->conn;
|
||||
struct xmpp_compression *comp = conn->compression.state;
|
||||
return _compression_write(conn, comp->compression.buffer, 0,
|
||||
conn->compression.dont_reset ? Z_SYNC_FLUSH
|
||||
: Z_FULL_FLUSH);
|
||||
}
|
||||
|
||||
static int compression_pending(struct conn_interface *intf)
|
||||
{
|
||||
xmpp_conn_t *conn = intf->conn;
|
||||
struct xmpp_compression *comp = conn->compression.state;
|
||||
return comp->decompression.stream.next_in != NULL ||
|
||||
comp->next.pending(intf);
|
||||
}
|
||||
|
||||
static int compression_get_error(struct conn_interface *intf)
|
||||
{
|
||||
struct conn_interface *next = &intf->conn->compression.state->next;
|
||||
return next->get_error(next);
|
||||
}
|
||||
|
||||
static int compression_is_recoverable(struct conn_interface *intf, int err)
|
||||
{
|
||||
struct conn_interface *next = &intf->conn->compression.state->next;
|
||||
return next->error_is_recoverable(next, err);
|
||||
}
|
||||
|
||||
static const struct conn_interface compression_intf = {
|
||||
compression_read,
|
||||
compression_write,
|
||||
compression_flush,
|
||||
compression_pending,
|
||||
compression_get_error,
|
||||
compression_is_recoverable,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void *_zlib_alloc(void *opaque, unsigned int items, unsigned int size)
|
||||
{
|
||||
size_t sz = items * size;
|
||||
/* Poor man's multiplication overflow check */
|
||||
if (sz < items || sz < size)
|
||||
return NULL;
|
||||
return strophe_alloc(opaque, sz);
|
||||
}
|
||||
|
||||
static void _init_zlib_compression(xmpp_ctx_t *ctx, struct zlib_compression *s)
|
||||
{
|
||||
s->buffer = strophe_alloc(ctx, STROPHE_COMPRESSION_BUFFER_SIZE);
|
||||
s->buffer_end = s->buffer + STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||
|
||||
s->stream.opaque = ctx;
|
||||
s->stream.zalloc = _zlib_alloc;
|
||||
s->stream.zfree = (free_func)strophe_free;
|
||||
}
|
||||
|
||||
int compression_init(xmpp_conn_t *conn)
|
||||
{
|
||||
if (!conn->compression.allowed || !conn->compression.supported)
|
||||
return -1;
|
||||
conn->compression.state =
|
||||
strophe_alloc(conn->ctx, sizeof(*conn->compression.state));
|
||||
struct xmpp_compression *comp = conn->compression.state;
|
||||
memset(comp, 0, sizeof(*comp));
|
||||
|
||||
comp->conn = conn;
|
||||
|
||||
comp->next = conn->intf;
|
||||
conn->intf = compression_intf;
|
||||
conn->intf.conn = conn;
|
||||
|
||||
_init_zlib_compression(conn->ctx, &comp->compression);
|
||||
|
||||
comp->compression.stream.next_out = comp->compression.buffer;
|
||||
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||
int ret = deflateInit(&comp->compression.stream, Z_DEFAULT_COMPRESSION);
|
||||
if (ret != Z_OK) {
|
||||
strophe_free_and_null(conn->ctx, comp->compression.buffer);
|
||||
conn->error = ret;
|
||||
conn_disconnect(conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
_init_zlib_compression(conn->ctx, &comp->decompression);
|
||||
|
||||
ret = inflateInit(&comp->decompression.stream);
|
||||
if (ret != Z_OK) {
|
||||
strophe_free_and_null(conn->ctx, comp->decompression.buffer);
|
||||
conn->error = ret;
|
||||
conn_disconnect(conn);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void compression_free(xmpp_conn_t *conn)
|
||||
{
|
||||
struct xmpp_compression *comp = conn->compression.state;
|
||||
if (!comp)
|
||||
return;
|
||||
if (comp->compression.buffer) {
|
||||
deflateEnd(&comp->compression.stream);
|
||||
strophe_free_and_null(conn->ctx, comp->compression.buffer);
|
||||
}
|
||||
if (comp->decompression.buffer) {
|
||||
inflateEnd(&comp->decompression.stream);
|
||||
strophe_free_and_null(conn->ctx, comp->decompression.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
|
||||
{
|
||||
if (strcasecmp(text, "zlib") == 0) {
|
||||
conn->compression.supported = 1;
|
||||
}
|
||||
}
|
||||
36
src/compression_dummy.c
Normal file
36
src/compression_dummy.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* compression_dummy.c
|
||||
** strophe XMPP client library -- Dummy Compression
|
||||
**
|
||||
** Copyright (C) 2024 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Dummy Compression.
|
||||
*/
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int compression_init(xmpp_conn_t *conn)
|
||||
{
|
||||
conn->compression.supported = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void compression_free(xmpp_conn_t *conn)
|
||||
{
|
||||
UNUSED(conn);
|
||||
}
|
||||
|
||||
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
|
||||
{
|
||||
UNUSED(text);
|
||||
conn->compression.supported = 0;
|
||||
}
|
||||
1829
src/conn.c
1829
src/conn.c
File diff suppressed because it is too large
Load Diff
44
src/crypto.c
44
src/crypto.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* crypto.c
|
||||
* strophe XMPP client library -- public interface for digests, encodings
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -22,11 +23,11 @@
|
||||
#include <assert.h>
|
||||
#include <string.h> /* memset, memcpy */
|
||||
|
||||
#include "common.h" /* xmpp_alloc */
|
||||
#include "common.h" /* strophe_alloc */
|
||||
#include "ostypes.h" /* uint8_t, size_t */
|
||||
#include "sha1.h"
|
||||
#include "snprintf.h" /* xmpp_snprintf */
|
||||
#include "strophe.h" /* xmpp_ctx_t, xmpp_free */
|
||||
#include "strophe.h" /* xmpp_ctx_t, strophe_free */
|
||||
|
||||
struct _xmpp_sha1_t {
|
||||
xmpp_ctx_t *xmpp_ctx;
|
||||
@@ -42,7 +43,7 @@ static char *digest_to_string(const uint8_t *digest, char *s, size_t len)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
|
||||
xmpp_snprintf(s + i * 2, 3, "%02x", digest[i]);
|
||||
strophe_snprintf(s + i * 2, 3, "%02x", digest[i]);
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -53,7 +54,7 @@ static char *digest_to_string_alloc(xmpp_ctx_t *ctx, const uint8_t *digest)
|
||||
size_t slen;
|
||||
|
||||
slen = SHA1_DIGEST_SIZE * 2 + 1;
|
||||
s = xmpp_alloc(ctx, slen);
|
||||
s = strophe_alloc(ctx, slen);
|
||||
if (s) {
|
||||
s = digest_to_string(digest, s, slen);
|
||||
assert(s != NULL);
|
||||
@@ -122,7 +123,7 @@ xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_sha1_t *sha1;
|
||||
|
||||
sha1 = xmpp_alloc(ctx, sizeof(*sha1));
|
||||
sha1 = strophe_alloc(ctx, sizeof(*sha1));
|
||||
if (sha1) {
|
||||
memset(sha1, 0, sizeof(*sha1));
|
||||
crypto_SHA1_Init(&sha1->ctx);
|
||||
@@ -139,7 +140,7 @@ xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
|
||||
*/
|
||||
void xmpp_sha1_free(xmpp_sha1_t *sha1)
|
||||
{
|
||||
xmpp_free(sha1->xmpp_ctx, sha1);
|
||||
strophe_free(sha1->xmpp_ctx, sha1);
|
||||
}
|
||||
|
||||
/** Update SHA1 context with the next portion of data.
|
||||
@@ -187,9 +188,9 @@ char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen)
|
||||
}
|
||||
|
||||
/** Return message digest rendered as a string.
|
||||
* Returns an allocated string. Free the string using the Strophe context
|
||||
* which is passed to xmpp_sha1_new(). Call this function after
|
||||
* xmpp_sha1_final().
|
||||
* Returns an allocated string. Free the string by calling xmpp_free() using
|
||||
* the Strophe context which is passed to xmpp_sha1_new(). Call this function
|
||||
* after xmpp_sha1_final().
|
||||
*
|
||||
* @param sha1 a SHA1 object
|
||||
*
|
||||
@@ -244,15 +245,14 @@ static const char _base64_charmap[65] = {
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};
|
||||
|
||||
static size_t base64_encoded_len(const size_t len)
|
||||
static size_t base64_encoded_len(size_t len)
|
||||
{
|
||||
/* encoded steam is 4 bytes for every three, rounded up */
|
||||
return ((len + 2) / 3) << 2;
|
||||
}
|
||||
|
||||
static char *base64_encode(xmpp_ctx_t *ctx,
|
||||
const unsigned char *const buffer,
|
||||
const size_t len)
|
||||
static char *
|
||||
base64_encode(xmpp_ctx_t *ctx, const unsigned char *buffer, size_t len)
|
||||
{
|
||||
size_t clen;
|
||||
char *cbuf, *c;
|
||||
@@ -260,7 +260,7 @@ static char *base64_encode(xmpp_ctx_t *ctx,
|
||||
size_t i;
|
||||
|
||||
clen = base64_encoded_len(len);
|
||||
cbuf = xmpp_alloc(ctx, clen + 1);
|
||||
cbuf = strophe_alloc(ctx, clen + 1);
|
||||
if (cbuf != NULL) {
|
||||
c = cbuf;
|
||||
/* loop over data, turning every 3 bytes into 4 characters */
|
||||
@@ -304,7 +304,7 @@ static char *base64_encode(xmpp_ctx_t *ctx,
|
||||
return cbuf;
|
||||
}
|
||||
|
||||
static size_t base64_decoded_len(const char *const buffer, const size_t len)
|
||||
static size_t base64_decoded_len(const char *buffer, size_t len)
|
||||
{
|
||||
size_t nudge = 0;
|
||||
unsigned char c;
|
||||
@@ -331,8 +331,8 @@ static size_t base64_decoded_len(const char *const buffer, const size_t len)
|
||||
}
|
||||
|
||||
static void base64_decode(xmpp_ctx_t *ctx,
|
||||
const char *const buffer,
|
||||
const size_t len,
|
||||
const char *buffer,
|
||||
size_t len,
|
||||
unsigned char **out,
|
||||
size_t *outlen)
|
||||
{
|
||||
@@ -349,7 +349,7 @@ static void base64_decode(xmpp_ctx_t *ctx,
|
||||
if (dlen == 0)
|
||||
goto _base64_error;
|
||||
|
||||
dbuf = xmpp_alloc(ctx, dlen + 1);
|
||||
dbuf = strophe_alloc(ctx, dlen + 1);
|
||||
if (dbuf != NULL) {
|
||||
d = dbuf;
|
||||
/* loop over each set of 4 characters, decoding 3 bytes */
|
||||
@@ -428,7 +428,7 @@ static void base64_decode(xmpp_ctx_t *ctx,
|
||||
|
||||
_base64_decode_error:
|
||||
/* invalid character; abort decoding! */
|
||||
xmpp_free(ctx, dbuf);
|
||||
strophe_free(ctx, dbuf);
|
||||
_base64_error:
|
||||
*out = NULL;
|
||||
*outlen = 0;
|
||||
@@ -470,7 +470,7 @@ char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
|
||||
|
||||
if (len == 0) {
|
||||
/* handle empty string */
|
||||
buf = xmpp_alloc(ctx, 1);
|
||||
buf = strophe_alloc(ctx, 1);
|
||||
if (buf)
|
||||
buf[0] = '\0';
|
||||
buflen = 0;
|
||||
@@ -479,7 +479,7 @@ char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
|
||||
}
|
||||
if (buf) {
|
||||
if (buflen != strlen((char *)buf)) {
|
||||
xmpp_free(ctx, buf);
|
||||
strophe_free(ctx, buf);
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
184
src/ctx.c
184
src/ctx.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* ctx.c
|
||||
** strophe XMPP client library -- run-time context implementation
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -50,10 +51,22 @@
|
||||
#include "resolver.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Workaround for systems without va_copy support. */
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800 || \
|
||||
!defined(_MSC_VER) && !defined(HAVE_DECL_VA_COPY)
|
||||
#define va_copy(d, s) (memcpy(&d, &s, sizeof(va_list)))
|
||||
#ifndef va_copy
|
||||
#ifdef HAVE_VA_COPY
|
||||
#define va_copy(dest, src) va_copy(dest, src)
|
||||
#else
|
||||
#ifdef HAVE___VA_COPY
|
||||
#define va_copy(dest, src) __va_copy(dest, src)
|
||||
#else
|
||||
#ifndef VA_LIST_IS_ARRAY
|
||||
#define va_copy(dest, src) (dest) = (src)
|
||||
#else
|
||||
#include <string.h>
|
||||
#define va_copy(dest, src) \
|
||||
memcpy((char *)(dest), (char *)(src), sizeof(va_list))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Initialize the Strophe library.
|
||||
@@ -122,19 +135,19 @@ int xmpp_version_check(int major, int minor)
|
||||
/* Wrap stdlib routines malloc, free, and realloc for default memory
|
||||
* management.
|
||||
*/
|
||||
static void *_malloc(const size_t size, void *const userdata)
|
||||
static void *_malloc(size_t size, void *userdata)
|
||||
{
|
||||
UNUSED(userdata);
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void _free(void *p, void *const userdata)
|
||||
static void _free(void *p, void *userdata)
|
||||
{
|
||||
UNUSED(userdata);
|
||||
free(p);
|
||||
}
|
||||
|
||||
static void *_realloc(void *p, const size_t size, void *const userdata)
|
||||
static void *_realloc(void *p, size_t size, void *userdata)
|
||||
{
|
||||
UNUSED(userdata);
|
||||
return realloc(p, size);
|
||||
@@ -146,8 +159,7 @@ static xmpp_mem_t xmpp_default_mem = {
|
||||
_free, _realloc, NULL};
|
||||
|
||||
/* log levels and names */
|
||||
static const char *const _xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN",
|
||||
"ERROR"};
|
||||
static const char *_xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
||||
static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
|
||||
XMPP_LEVEL_DEBUG, XMPP_LEVEL_INFO, XMPP_LEVEL_WARN, XMPP_LEVEL_ERROR};
|
||||
|
||||
@@ -160,10 +172,10 @@ static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
|
||||
* @param area the area the log message is for
|
||||
* @param msg the log message
|
||||
*/
|
||||
static void xmpp_default_logger(void *const userdata,
|
||||
const xmpp_log_level_t level,
|
||||
const char *const area,
|
||||
const char *const msg)
|
||||
static void xmpp_default_logger(void *userdata,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *msg)
|
||||
{
|
||||
xmpp_log_level_t filter_level = *(xmpp_log_level_t *)userdata;
|
||||
if (level >= filter_level)
|
||||
@@ -212,7 +224,7 @@ static xmpp_log_t xmpp_default_log = {NULL, NULL};
|
||||
*
|
||||
* @return a pointer to the allocated memory or NULL on an error
|
||||
*/
|
||||
void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size)
|
||||
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size)
|
||||
{
|
||||
return ctx->mem->alloc(size, ctx->mem->userdata);
|
||||
}
|
||||
@@ -223,11 +235,21 @@ void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size)
|
||||
* @param ctx a Strophe context object
|
||||
* @param p a pointer referencing memory to be freed
|
||||
*/
|
||||
void xmpp_free(const xmpp_ctx_t *const ctx, void *p)
|
||||
void strophe_free(const xmpp_ctx_t *ctx, void *p)
|
||||
{
|
||||
ctx->mem->free(p, ctx->mem->userdata);
|
||||
}
|
||||
|
||||
/** Trampoline to \ref strophe_free
|
||||
*
|
||||
* @param ctx \ref strophe_free
|
||||
* @param p \ref strophe_free
|
||||
*/
|
||||
void xmpp_free(const xmpp_ctx_t *ctx, void *p)
|
||||
{
|
||||
strophe_free(ctx, p);
|
||||
}
|
||||
|
||||
/** Reallocate memory in a Strophe context.
|
||||
* All Strophe functions will use this to reallocate memory.
|
||||
*
|
||||
@@ -237,7 +259,7 @@ void xmpp_free(const xmpp_ctx_t *const ctx, void *p)
|
||||
*
|
||||
* @return a pointer to the reallocated memory or NULL on an error
|
||||
*/
|
||||
void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size)
|
||||
void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
|
||||
{
|
||||
return ctx->mem->realloc(p, size, ctx->mem->userdata);
|
||||
}
|
||||
@@ -246,8 +268,8 @@ void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size)
|
||||
* Write a log message to the logger for the context for the specified
|
||||
* level and area. This function takes a printf-style format string and a
|
||||
* variable argument list (in va_list) format. This function is not meant
|
||||
* to be called directly, but is used via xmpp_error, xmpp_warn, xmpp_info,
|
||||
* and xmpp_debug.
|
||||
* to be called directly, but is used via strophe_error, strophe_warn,
|
||||
* strophe_info, and strophe_debug.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param level the level at which to log
|
||||
@@ -255,32 +277,40 @@ void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size)
|
||||
* @param fmt a printf-style format string for the message
|
||||
* @param ap variable argument list supplied for the format string
|
||||
*/
|
||||
void xmpp_log(const xmpp_ctx_t *const ctx,
|
||||
const xmpp_log_level_t level,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
va_list ap)
|
||||
static void _strophe_log(const xmpp_ctx_t *ctx,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
int oldret, ret;
|
||||
char smbuf[1024];
|
||||
char *buf;
|
||||
va_list copy;
|
||||
|
||||
if (!ctx->log->handler)
|
||||
return;
|
||||
|
||||
if (ctx->log->handler == xmpp_default_logger &&
|
||||
level < *(xmpp_log_level_t *)ctx->log->userdata)
|
||||
return;
|
||||
|
||||
va_copy(copy, ap);
|
||||
ret = xmpp_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
|
||||
ret = strophe_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
|
||||
if (ret >= (int)sizeof(smbuf)) {
|
||||
buf = (char *)xmpp_alloc(ctx, ret + 1);
|
||||
buf = (char *)strophe_alloc(ctx, ret + 1);
|
||||
if (!buf) {
|
||||
buf = NULL;
|
||||
xmpp_error(ctx, "log", "Failed allocating memory for log message.");
|
||||
strophe_error(ctx, "log",
|
||||
"Failed allocating memory for log message.");
|
||||
va_end(copy);
|
||||
return;
|
||||
}
|
||||
oldret = ret;
|
||||
ret = xmpp_vsnprintf(buf, ret + 1, fmt, copy);
|
||||
ret = strophe_vsnprintf(buf, ret + 1, fmt, copy);
|
||||
if (ret > oldret) {
|
||||
xmpp_error(ctx, "log", "Unexpected error");
|
||||
xmpp_free(ctx, buf);
|
||||
strophe_error(ctx, "log", "Unexpected error");
|
||||
strophe_free(ctx, buf);
|
||||
va_end(copy);
|
||||
return;
|
||||
}
|
||||
@@ -289,11 +319,20 @@ void xmpp_log(const xmpp_ctx_t *const ctx,
|
||||
}
|
||||
va_end(copy);
|
||||
|
||||
if (ctx->log->handler)
|
||||
ctx->log->handler(ctx->log->userdata, level, area, buf);
|
||||
ctx->log->handler(ctx->log->userdata, level, area, buf);
|
||||
|
||||
if (buf != smbuf)
|
||||
xmpp_free(ctx, buf);
|
||||
strophe_free(ctx, buf);
|
||||
}
|
||||
|
||||
/* Dummy trampoline, will be removed when deprecated.c is deleted */
|
||||
void strophe_log_internal(const xmpp_ctx_t *ctx,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
_strophe_log(ctx, level, area, fmt, ap);
|
||||
}
|
||||
|
||||
/** Write to the log at the ERROR level.
|
||||
@@ -306,15 +345,15 @@ void xmpp_log(const xmpp_ctx_t *const ctx,
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*/
|
||||
void xmpp_error(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...)
|
||||
void strophe_error(const xmpp_ctx_t *ctx,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
||||
_strophe_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
@@ -328,15 +367,12 @@ void xmpp_error(const xmpp_ctx_t *const ctx,
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*/
|
||||
void xmpp_warn(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...)
|
||||
void strophe_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
||||
_strophe_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
@@ -350,15 +386,12 @@ void xmpp_warn(const xmpp_ctx_t *const ctx,
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*/
|
||||
void xmpp_info(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...)
|
||||
void strophe_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
||||
_strophe_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
@@ -372,15 +405,39 @@ void xmpp_info(const xmpp_ctx_t *const ctx,
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*/
|
||||
void xmpp_debug(const xmpp_ctx_t *const ctx,
|
||||
const char *const area,
|
||||
const char *const fmt,
|
||||
...)
|
||||
void strophe_debug(const xmpp_ctx_t *ctx,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||
_strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** Write to the log at the DEBUG level if verbosity is enabled.
|
||||
* This is a convenience function for writing to the log at the DEBUG level.
|
||||
* It takes a printf-style format string followed by a variable list of
|
||||
* arguments for formatting.
|
||||
*
|
||||
* @param level the verbosity level
|
||||
* @param ctx a Strophe context object
|
||||
* @param area the area to log for
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*/
|
||||
void strophe_debug_verbose(
|
||||
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (ctx->verbosity < level)
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
@@ -398,8 +455,7 @@ void xmpp_debug(const xmpp_ctx_t *const ctx,
|
||||
*
|
||||
* @ingroup Context
|
||||
*/
|
||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
|
||||
const xmpp_log_t *const log)
|
||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log)
|
||||
{
|
||||
xmpp_ctx_t *ctx = NULL;
|
||||
|
||||
@@ -409,6 +465,8 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
|
||||
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
|
||||
|
||||
if (ctx != NULL) {
|
||||
memset(ctx, 0, sizeof(xmpp_ctx_t));
|
||||
|
||||
if (mem != NULL)
|
||||
ctx->mem = mem;
|
||||
else
|
||||
@@ -419,13 +477,11 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
|
||||
else
|
||||
ctx->log = log;
|
||||
|
||||
ctx->connlist = NULL;
|
||||
ctx->timed_handlers = NULL;
|
||||
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
||||
ctx->rand = xmpp_rand_new(ctx);
|
||||
ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;
|
||||
if (ctx->rand == NULL) {
|
||||
xmpp_free(ctx, ctx);
|
||||
strophe_free(ctx, ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
}
|
||||
@@ -439,21 +495,21 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
|
||||
*
|
||||
* @ingroup Context
|
||||
*/
|
||||
void xmpp_ctx_free(xmpp_ctx_t *const ctx)
|
||||
void xmpp_ctx_free(xmpp_ctx_t *ctx)
|
||||
{
|
||||
/* mem and log are owned by their suppliers */
|
||||
xmpp_rand_free(ctx, ctx->rand);
|
||||
xmpp_free(ctx, ctx); /* pull the hole in after us */
|
||||
strophe_free(ctx, ctx); /* pull the hole in after us */
|
||||
}
|
||||
|
||||
/** Set the timeout to use when calling xmpp_run().
|
||||
/** Set the verbosity level of a Strophe context.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param timeout the time to wait for events in milliseconds
|
||||
* @param level the verbosity level
|
||||
*
|
||||
* @ingroup Context
|
||||
*/
|
||||
void xmpp_ctx_set_timeout(xmpp_ctx_t *const ctx, const unsigned long timeout)
|
||||
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level)
|
||||
{
|
||||
ctx->timeout = timeout;
|
||||
ctx->verbosity = level;
|
||||
}
|
||||
|
||||
276
src/deprecated.c
Normal file
276
src/deprecated.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* deprecated.c
|
||||
** strophe XMPP client library -- File with deprecated API functions.
|
||||
**
|
||||
** Copyright (C) 2022 Steffen Jaeckel
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* File with deprecated API functions.
|
||||
*/
|
||||
|
||||
/** @defgroup Deprecated All deprecated functions
|
||||
* These functions will be removed in the next release.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/** Allocate memory in a Strophe context.
|
||||
* All Strophe functions will use this to allocate memory.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param size the number of bytes to allocate
|
||||
*
|
||||
* @return a pointer to the allocated memory or NULL on an error
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size)
|
||||
{
|
||||
return strophe_alloc(ctx, size);
|
||||
}
|
||||
|
||||
/** Reallocate memory in a Strophe context.
|
||||
* All Strophe functions will use this to reallocate memory.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param p a pointer to previously allocated memory
|
||||
* @param size the new size in bytes to allocate
|
||||
*
|
||||
* @return a pointer to the reallocated memory or NULL on an error
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void *xmpp_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
|
||||
{
|
||||
return strophe_realloc(ctx, p, size);
|
||||
}
|
||||
|
||||
/** implement our own strdup that uses the ctx allocator */
|
||||
/** Duplicate a string.
|
||||
* This function replaces the standard strdup library call with a version
|
||||
* that uses the Strophe context object's allocator.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param s a string
|
||||
*
|
||||
* @return a newly allocated string with the same data as s or NULL on error
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
char *xmpp_strdup(const xmpp_ctx_t *ctx, const char *s)
|
||||
{
|
||||
return strophe_strdup(ctx, s);
|
||||
}
|
||||
|
||||
/** Duplicate a string with a maximum length.
|
||||
* This function replaces the standard strndup library call with a version
|
||||
* that uses the Strophe context object's allocator.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param s a string
|
||||
* @param len the maximum length of the string to copy
|
||||
*
|
||||
* @return a newly allocated string that contains at most `len` symbols
|
||||
* of the original string or NULL on error
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
|
||||
{
|
||||
return strophe_strndup(ctx, s, len);
|
||||
}
|
||||
|
||||
void xmpp_log(const xmpp_ctx_t *ctx,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
strophe_log_internal(ctx, level, area, fmt, ap);
|
||||
}
|
||||
|
||||
/** Write to the log at the ERROR level.
|
||||
* This is a convenience function for writing to the log at the
|
||||
* ERROR level. It takes a printf-style format string followed by a
|
||||
* variable list of arguments for formatting.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param area the area to log for
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void xmpp_error(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
strophe_log_internal(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** Write to the log at the WARN level.
|
||||
* This is a convenience function for writing to the log at the WARN level.
|
||||
* It takes a printf-style format string followed by a variable list of
|
||||
* arguments for formatting.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param area the area to log for
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void xmpp_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
strophe_log_internal(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** Write to the log at the INFO level.
|
||||
* This is a convenience function for writing to the log at the INFO level.
|
||||
* It takes a printf-style format string followed by a variable list of
|
||||
* arguments for formatting.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param area the area to log for
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void xmpp_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
strophe_log_internal(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** Write to the log at the DEBUG level.
|
||||
* This is a convenience function for writing to the log at the DEBUG level.
|
||||
* It takes a printf-style format string followed by a variable list of
|
||||
* arguments for formatting.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param area the area to log for
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void xmpp_debug(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
strophe_log_internal(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** Write to the log at the DEBUG level if verbosity is enabled.
|
||||
* This is a convenience function for writing to the log at the DEBUG level.
|
||||
* It takes a printf-style format string followed by a variable list of
|
||||
* arguments for formatting.
|
||||
*
|
||||
* @param level the verbosity level
|
||||
* @param ctx a Strophe context object
|
||||
* @param area the area to log for
|
||||
* @param fmt a printf-style format string followed by a variable list of
|
||||
* arguments to format
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void xmpp_debug_verbose(
|
||||
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (ctx->verbosity < level)
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
strophe_log_internal(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** strtok_r(3) implementation.
|
||||
* This function has appeared in POSIX.1-2001, but not in C standard.
|
||||
* For example, visual studio older than 2005 doesn't provide strtok_r()
|
||||
* nor strtok_s().
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
|
||||
{
|
||||
return strophe_strtok_r(s, delim, saveptr);
|
||||
}
|
||||
|
||||
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = strophe_vsnprintf(str, count, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg)
|
||||
{
|
||||
return strophe_vsnprintf(str, count, fmt, arg);
|
||||
}
|
||||
|
||||
/** Set TCP keepalive parameters
|
||||
* Turn on TCP keepalive and set timeout and interval. Zero timeout
|
||||
* disables TCP keepalives. The parameters are applied immediately for
|
||||
* a non disconnected object. Also, they are applied when the connection
|
||||
* object connects successfully.
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
* @param timeout TCP keepalive timeout in seconds
|
||||
* @param interval TCP keepalive interval in seconds
|
||||
*
|
||||
* @note this function is deprecated
|
||||
* @see xmpp_conn_set_sockopt_callback()
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval)
|
||||
{
|
||||
conn->ka_timeout = timeout;
|
||||
conn->ka_interval = interval;
|
||||
conn->ka_count = 0;
|
||||
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive);
|
||||
}
|
||||
|
||||
/** Disable TLS for this connection, called by users of the library.
|
||||
* Occasionally a server will be misconfigured to send the starttls
|
||||
* feature, but will not support the handshake.
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
*
|
||||
* @note this function is deprecated
|
||||
* @see xmpp_conn_set_flags()
|
||||
*
|
||||
* @ingroup Deprecated
|
||||
*/
|
||||
void xmpp_conn_disable_tls(xmpp_conn_t *conn)
|
||||
{
|
||||
long flags = xmpp_conn_get_flags(conn);
|
||||
|
||||
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||
(void)xmpp_conn_set_flags(conn, flags);
|
||||
}
|
||||
155
src/event.c
155
src/event.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* event.c
|
||||
** strophe XMPP client library -- event loop and management
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -38,7 +39,7 @@
|
||||
#include <sys/select.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#define _sleep(x) usleep((x)*1000)
|
||||
#define _sleep(x) usleep((x) * 1000)
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#ifndef ETIMEDOUT
|
||||
@@ -57,8 +58,22 @@
|
||||
#include "common.h"
|
||||
#include "parser.h"
|
||||
|
||||
#ifndef STROPHE_MESSAGE_BUFFER_SIZE
|
||||
/** Max buffer size for receiving messages. */
|
||||
#define STROPE_MESSAGE_BUFFER_SIZE 4096
|
||||
#define STROPHE_MESSAGE_BUFFER_SIZE 4096
|
||||
#endif
|
||||
|
||||
static int _connect_next(xmpp_conn_t *conn)
|
||||
{
|
||||
sock_close(conn->sock);
|
||||
conn->sock = sock_connect(conn->xsock);
|
||||
if (conn->sock == INVALID_SOCKET)
|
||||
return -1;
|
||||
|
||||
conn->timeout_stamp = time_stamp();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Run the event loop once.
|
||||
* This function will run send any data that has been queued by
|
||||
@@ -73,17 +88,18 @@
|
||||
*
|
||||
* @ingroup EventLoop
|
||||
*/
|
||||
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
|
||||
{
|
||||
xmpp_connlist_t *connitem;
|
||||
xmpp_conn_t *conn;
|
||||
struct conn_interface *intf;
|
||||
fd_set rfds, wfds;
|
||||
sock_t max = 0;
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
xmpp_send_queue_t *sq, *tsq;
|
||||
int towrite;
|
||||
char buf[STROPE_MESSAGE_BUFFER_SIZE];
|
||||
char buf[STROPHE_MESSAGE_BUFFER_SIZE];
|
||||
uint64_t next;
|
||||
uint64_t usec;
|
||||
int tls_read_bytes = 0;
|
||||
@@ -99,17 +115,21 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
connitem = connitem->next;
|
||||
continue;
|
||||
}
|
||||
intf = &conn->intf;
|
||||
|
||||
/* if we're running tls, there may be some remaining data waiting to
|
||||
* be sent, so push that out */
|
||||
if (conn->tls) {
|
||||
ret = tls_clear_pending_write(conn->tls);
|
||||
ret = tls_clear_pending_write(intf);
|
||||
|
||||
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
|
||||
if (ret < 0 && !tls_is_recoverable(intf, tls_error(intf))) {
|
||||
/* an error occurred */
|
||||
xmpp_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
|
||||
strophe_debug(
|
||||
ctx, "xmpp",
|
||||
"Send error of pending data occurred, disconnecting.");
|
||||
conn->error = ECONNABORTED;
|
||||
conn_disconnect(conn);
|
||||
goto next_item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,43 +138,55 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
while (sq) {
|
||||
towrite = sq->len - sq->written;
|
||||
|
||||
if (conn->tls) {
|
||||
ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
|
||||
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls)))
|
||||
conn->error = tls_error(conn->tls);
|
||||
} else {
|
||||
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
|
||||
if (ret < 0 && !sock_is_recoverable(sock_error()))
|
||||
conn->error = sock_error();
|
||||
}
|
||||
ret = conn_interface_write(intf, &sq->data[sq->written], towrite);
|
||||
if (ret > 0 && ret < towrite)
|
||||
sq->written += ret; /* not all data could be sent now */
|
||||
sq->wip = 1;
|
||||
if (ret != towrite)
|
||||
break; /* partial write or an error */
|
||||
|
||||
/* all data for this queue item written, delete and move on */
|
||||
xmpp_free(ctx, sq->data);
|
||||
strophe_debug(conn->ctx, "conn", "SENT: %s", sq->data);
|
||||
strophe_debug_verbose(1, ctx, "xmpp", "Q_SENT: %p", sq);
|
||||
tsq = sq;
|
||||
sq = sq->next;
|
||||
conn->send_queue_len--;
|
||||
xmpp_free(ctx, tsq);
|
||||
if (tsq->owner & XMPP_QUEUE_USER)
|
||||
conn->send_queue_user_len--;
|
||||
if (!(tsq->owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled) {
|
||||
tsq->sm_h = conn->sm_state->sm_sent_nr;
|
||||
conn->sm_state->sm_sent_nr++;
|
||||
strophe_debug_verbose(1, ctx, "xmpp", "SM_Q_MOVE: %p, h=%lu",
|
||||
tsq, tsq->sm_h);
|
||||
add_queue_back(&conn->sm_state->sm_queue, tsq);
|
||||
tsq = NULL;
|
||||
}
|
||||
if (tsq) {
|
||||
strophe_debug_verbose(2, ctx, "xmpp", "Q_FREE: %p", tsq);
|
||||
strophe_debug_verbose(3, ctx, "conn", "Q_CONTENT: %s",
|
||||
tsq->data);
|
||||
strophe_free(ctx, tsq->data);
|
||||
strophe_free(ctx, tsq);
|
||||
}
|
||||
|
||||
/* pop the top item */
|
||||
conn->send_queue_head = sq;
|
||||
/* if we've sent everything update the tail */
|
||||
if (!sq)
|
||||
conn->send_queue_tail = NULL;
|
||||
trigger_sm_callback(conn);
|
||||
}
|
||||
intf->flush(intf);
|
||||
|
||||
/* tear down connection on error */
|
||||
if (conn->error) {
|
||||
/* FIXME: need to tear down send queues and random other things
|
||||
* maybe this should be abstracted */
|
||||
xmpp_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
|
||||
strophe_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
|
||||
conn->error = ECONNABORTED;
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
|
||||
next_item:
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
@@ -179,6 +211,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
intf = &conn->intf;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
@@ -190,9 +223,14 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
conn->connect_timeout)
|
||||
FD_SET(conn->sock, &wfds);
|
||||
else {
|
||||
conn->error = ETIMEDOUT;
|
||||
xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
|
||||
conn_disconnect(conn);
|
||||
strophe_info(ctx, "xmpp", "Connection attempt timed out.");
|
||||
ret = _connect_next(conn);
|
||||
if (ret != 0) {
|
||||
conn->error = ETIMEDOUT;
|
||||
conn_disconnect(conn);
|
||||
} else {
|
||||
FD_SET(conn->sock, &wfds);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XMPP_STATE_CONNECTED:
|
||||
@@ -208,7 +246,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
|
||||
/* Check if there is something in the SSL buffer. */
|
||||
if (conn->tls)
|
||||
tls_read_bytes += tls_pending(conn->tls);
|
||||
tls_read_bytes += tls_pending(intf);
|
||||
|
||||
if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
|
||||
max = conn->sock;
|
||||
@@ -227,9 +265,9 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
|
||||
/* select errored */
|
||||
if (ret < 0) {
|
||||
if (!sock_is_recoverable(sock_error()))
|
||||
xmpp_error(ctx, "xmpp", "event watcher internal error %d",
|
||||
sock_error());
|
||||
if (!sock_is_recoverable(NULL, sock_error(NULL)))
|
||||
strophe_error(ctx, "xmpp", "event watcher internal error %d",
|
||||
sock_error(NULL));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -241,6 +279,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
intf = &conn->intf;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
@@ -251,47 +290,45 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
ret = sock_connect_error(conn->sock);
|
||||
if (ret != 0) {
|
||||
/* connection failed */
|
||||
xmpp_debug(ctx, "xmpp", "connection failed, error %d", ret);
|
||||
conn_disconnect(conn);
|
||||
strophe_debug(ctx, "xmpp", "connection failed, error %d",
|
||||
ret);
|
||||
ret = _connect_next(conn);
|
||||
if (ret != 0) {
|
||||
conn->error = ret;
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
conn->state = XMPP_STATE_CONNECTED;
|
||||
xmpp_debug(ctx, "xmpp", "connection successful");
|
||||
strophe_debug(ctx, "xmpp", "connection successful");
|
||||
conn_established(conn);
|
||||
}
|
||||
|
||||
break;
|
||||
case XMPP_STATE_CONNECTED:
|
||||
if (FD_ISSET(conn->sock, &rfds) ||
|
||||
(conn->tls && tls_pending(conn->tls))) {
|
||||
if (conn->tls) {
|
||||
ret = tls_read(conn->tls, buf, STROPE_MESSAGE_BUFFER_SIZE);
|
||||
} else {
|
||||
ret =
|
||||
sock_read(conn->sock, buf, STROPE_MESSAGE_BUFFER_SIZE);
|
||||
}
|
||||
if (FD_ISSET(conn->sock, &rfds) || intf->pending(intf)) {
|
||||
|
||||
ret = intf->read(intf, buf, STROPHE_MESSAGE_BUFFER_SIZE);
|
||||
|
||||
if (ret > 0) {
|
||||
ret = parser_feed(conn->parser, buf, ret);
|
||||
if (!ret) {
|
||||
xmpp_debug(ctx, "xmpp", "parse error [%s]", buf);
|
||||
strophe_debug(ctx, "xmpp", "parse error [%s]", buf);
|
||||
xmpp_send_error(conn, XMPP_SE_INVALID_XML,
|
||||
"parse error");
|
||||
}
|
||||
} else {
|
||||
if (conn->tls) {
|
||||
if (!tls_is_recoverable(tls_error(conn->tls))) {
|
||||
xmpp_debug(ctx, "xmpp",
|
||||
"Unrecoverable TLS error, %d.",
|
||||
tls_error(conn->tls));
|
||||
conn->error = tls_error(conn->tls);
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
} else {
|
||||
int err = intf->get_error(intf);
|
||||
if (!intf->error_is_recoverable(intf, err)) {
|
||||
strophe_debug(ctx, "xmpp", "Unrecoverable error: %d.",
|
||||
err);
|
||||
conn->error = err;
|
||||
conn_disconnect(conn);
|
||||
} else if (!conn->tls) {
|
||||
/* return of 0 means socket closed by server */
|
||||
xmpp_debug(ctx, "xmpp",
|
||||
"Socket closed by remote host.");
|
||||
strophe_debug(ctx, "xmpp",
|
||||
"Socket closed by remote host.");
|
||||
conn->error = ECONNRESET;
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
@@ -333,7 +370,7 @@ void xmpp_run(xmpp_ctx_t *ctx)
|
||||
/* make it possible to start event loop again */
|
||||
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
||||
|
||||
xmpp_debug(ctx, "event", "Event loop completed.");
|
||||
strophe_debug(ctx, "event", "Event loop completed.");
|
||||
}
|
||||
|
||||
/** Stop the event loop.
|
||||
@@ -346,8 +383,20 @@ void xmpp_run(xmpp_ctx_t *ctx)
|
||||
*/
|
||||
void xmpp_stop(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_debug(ctx, "event", "Stopping event loop.");
|
||||
strophe_debug(ctx, "event", "Stopping event loop.");
|
||||
|
||||
if (ctx->loop_status == XMPP_LOOP_RUNNING)
|
||||
ctx->loop_status = XMPP_LOOP_QUIT;
|
||||
}
|
||||
|
||||
/** Set the timeout to use when calling xmpp_run().
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param timeout the time to wait for events in milliseconds
|
||||
*
|
||||
* @ingroup EventLoop
|
||||
*/
|
||||
void xmpp_ctx_set_timeout(xmpp_ctx_t *ctx, unsigned long timeout)
|
||||
{
|
||||
ctx->timeout = timeout;
|
||||
}
|
||||
|
||||
246
src/handler.c
246
src/handler.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* handler.c
|
||||
** strophe XMPP client library -- event handler management
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -24,8 +25,6 @@
|
||||
#include "common.h"
|
||||
#include "ostypes.h"
|
||||
|
||||
typedef int (*xmpp_void_handler)();
|
||||
|
||||
/* Remove item from the list pointed by head, but don't free it.
|
||||
* There can be a situation when user's handler deletes another handler which
|
||||
* is the previous in the list. handler_fire_stanza() and handler_fire_timed()
|
||||
@@ -46,6 +45,17 @@ static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item)
|
||||
}
|
||||
}
|
||||
|
||||
static void _free_handlist_item(xmpp_ctx_t *ctx, xmpp_handlist_t *item)
|
||||
{
|
||||
if (item->u.ns)
|
||||
strophe_free(ctx, item->u.ns);
|
||||
if (item->u.name)
|
||||
strophe_free(ctx, item->u.name);
|
||||
if (item->u.type)
|
||||
strophe_free(ctx, item->u.type);
|
||||
strophe_free(ctx, item);
|
||||
}
|
||||
|
||||
/** Fire off all stanza handlers that match.
|
||||
* This function is called internally by the event loop whenever stanzas
|
||||
* are received from the XMPP server.
|
||||
@@ -53,7 +63,7 @@ static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item)
|
||||
* @param conn a Strophe connection object
|
||||
* @param stanza a Strophe stanza object
|
||||
*/
|
||||
void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
|
||||
void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
|
||||
{
|
||||
xmpp_handlist_t *item, *next, *head, *head_old;
|
||||
const char *id, *ns, *name, *type;
|
||||
@@ -69,9 +79,9 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
|
||||
|
||||
item = head;
|
||||
while (item) {
|
||||
/* don't fire user handlers until authentication succeeds and
|
||||
/* don't fire user handlers until stream negotiation has completed
|
||||
and skip newly added handlers */
|
||||
if ((item->user_handler && !conn->authenticated) ||
|
||||
if ((item->user_handler && !conn->stream_negotiation_completed) ||
|
||||
!item->enabled) {
|
||||
item = item->next;
|
||||
continue;
|
||||
@@ -87,8 +97,8 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
|
||||
/* replace old value */
|
||||
hash_add(conn->id_handlers, id, head);
|
||||
}
|
||||
xmpp_free(conn->ctx, item->u.id);
|
||||
xmpp_free(conn->ctx, item);
|
||||
strophe_free(conn->ctx, item->u.id);
|
||||
strophe_free(conn->ctx, item);
|
||||
}
|
||||
item = next;
|
||||
}
|
||||
@@ -105,9 +115,10 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
|
||||
|
||||
item = conn->handlers;
|
||||
while (item) {
|
||||
/* don't fire user handlers until authentication succeeds and
|
||||
/* don't fire user handlers until stream negotiation has completed and
|
||||
skip newly added handlers */
|
||||
if ((item->user_handler && !conn->authenticated) || !item->enabled) {
|
||||
if ((item->user_handler && !conn->stream_negotiation_completed) ||
|
||||
!item->enabled) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
@@ -124,13 +135,7 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
|
||||
if (!ret) {
|
||||
/* handler is one-shot, so delete it */
|
||||
_handler_item_remove(&conn->handlers, item);
|
||||
if (item->u.ns)
|
||||
xmpp_free(conn->ctx, item->u.ns);
|
||||
if (item->u.name)
|
||||
xmpp_free(conn->ctx, item->u.name);
|
||||
if (item->u.type)
|
||||
xmpp_free(conn->ctx, item->u.type);
|
||||
xmpp_free(conn->ctx, item);
|
||||
_free_handlist_item(conn->ctx, item);
|
||||
}
|
||||
}
|
||||
item = next;
|
||||
@@ -144,7 +149,7 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @return the time in milliseconds until the next handler will be ready
|
||||
*/
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_connlist_t *connitem;
|
||||
xmpp_handlist_t *item, *next;
|
||||
@@ -169,9 +174,9 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
|
||||
|
||||
item = conn->timed_handlers;
|
||||
while (item) {
|
||||
/* don't fire user handlers until authentication succeeds and
|
||||
skip newly added handlers */
|
||||
if ((item->user_handler && !conn->authenticated) ||
|
||||
/* don't fire user handlers until stream negotiation has completed
|
||||
and skip newly added handlers */
|
||||
if ((item->user_handler && !conn->stream_negotiation_completed) ||
|
||||
!item->enabled) {
|
||||
item = item->next;
|
||||
continue;
|
||||
@@ -189,7 +194,7 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
|
||||
if (!ret) {
|
||||
/* delete handler if it returned false */
|
||||
_handler_item_remove(&conn->timed_handlers, item);
|
||||
xmpp_free(ctx, item);
|
||||
strophe_free(ctx, item);
|
||||
}
|
||||
} else if (min > (item->u.period - elapsed))
|
||||
min = item->u.period - elapsed;
|
||||
@@ -220,7 +225,7 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
|
||||
if (!ret) {
|
||||
/* delete handler if it returned false */
|
||||
_handler_item_remove(&ctx->timed_handlers, item);
|
||||
xmpp_free(ctx, item);
|
||||
strophe_free(ctx, item);
|
||||
}
|
||||
} else if (min > (item->u.period - elapsed))
|
||||
min = item->u.period - elapsed;
|
||||
@@ -253,16 +258,16 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only)
|
||||
static void _timed_handler_add(xmpp_ctx_t *ctx,
|
||||
xmpp_handlist_t **handlers_list,
|
||||
xmpp_void_handler handler,
|
||||
const unsigned long period,
|
||||
void *const userdata,
|
||||
const int user_handler)
|
||||
unsigned long period,
|
||||
void *userdata,
|
||||
int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item;
|
||||
|
||||
/* check if handler is already in the list */
|
||||
for (item = *handlers_list; item; item = item->next) {
|
||||
if (item->handler == handler && item->userdata == userdata) {
|
||||
xmpp_warn(ctx, "xmpp", "Timed handler already exists.");
|
||||
strophe_warn(ctx, "xmpp", "Timed handler already exists.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -270,7 +275,7 @@ static void _timed_handler_add(xmpp_ctx_t *ctx,
|
||||
return;
|
||||
|
||||
/* build new item */
|
||||
item = xmpp_alloc(ctx, sizeof(xmpp_handlist_t));
|
||||
item = strophe_alloc(ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
@@ -297,7 +302,7 @@ static void _timed_handler_delete(xmpp_ctx_t *ctx,
|
||||
item = *handlers_list;
|
||||
if (item->handler == handler) {
|
||||
*handlers_list = item->next;
|
||||
xmpp_free(ctx, item);
|
||||
strophe_free(ctx, item);
|
||||
} else {
|
||||
handlers_list = &item->next;
|
||||
}
|
||||
@@ -311,16 +316,15 @@ static void _timed_handler_delete(xmpp_ctx_t *ctx,
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_timed_handler_delete(xmpp_conn_t *const conn,
|
||||
xmpp_timed_handler handler)
|
||||
void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler)
|
||||
{
|
||||
_timed_handler_delete(conn->ctx, &conn->timed_handlers, handler);
|
||||
}
|
||||
|
||||
static void _id_handler_add(xmpp_conn_t *const conn,
|
||||
static void _id_handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const id,
|
||||
void *const userdata,
|
||||
const char *id,
|
||||
void *userdata,
|
||||
int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
@@ -329,7 +333,7 @@ static void _id_handler_add(xmpp_conn_t *const conn,
|
||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
while (item) {
|
||||
if (item->handler == handler && item->userdata == userdata) {
|
||||
xmpp_warn(conn->ctx, "xmpp", "Id handler already exists.");
|
||||
strophe_warn(conn->ctx, "xmpp", "Id handler already exists.");
|
||||
break;
|
||||
}
|
||||
item = item->next;
|
||||
@@ -338,7 +342,7 @@ static void _id_handler_add(xmpp_conn_t *const conn,
|
||||
return;
|
||||
|
||||
/* build new item */
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
item = strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
@@ -348,9 +352,9 @@ static void _id_handler_add(xmpp_conn_t *const conn,
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
item->u.id = xmpp_strdup(conn->ctx, id);
|
||||
item->u.id = strophe_strdup(conn->ctx, id);
|
||||
if (!item->u.id) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
strophe_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -373,9 +377,9 @@ static void _id_handler_add(xmpp_conn_t *const conn,
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_id_handler_delete(xmpp_conn_t *const conn,
|
||||
void xmpp_id_handler_delete(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const id)
|
||||
const char *id)
|
||||
{
|
||||
xmpp_handlist_t *item, *prev, *next;
|
||||
|
||||
@@ -395,8 +399,8 @@ void xmpp_id_handler_delete(xmpp_conn_t *const conn,
|
||||
hash_add(conn->id_handlers, id, next);
|
||||
}
|
||||
|
||||
xmpp_free(conn->ctx, item->u.id);
|
||||
xmpp_free(conn->ctx, item);
|
||||
strophe_free(conn->ctx, item->u.id);
|
||||
strophe_free(conn->ctx, item);
|
||||
item = next;
|
||||
} else {
|
||||
prev = item;
|
||||
@@ -405,13 +409,23 @@ void xmpp_id_handler_delete(xmpp_conn_t *const conn,
|
||||
}
|
||||
}
|
||||
|
||||
static int _dup_string(xmpp_ctx_t *ctx, const char *src, char **dest)
|
||||
{
|
||||
if (src) {
|
||||
*dest = strophe_strdup(ctx, src);
|
||||
if (!(*dest))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add a stanza handler */
|
||||
static void _handler_add(xmpp_conn_t *const conn,
|
||||
static void _handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const ns,
|
||||
const char *const name,
|
||||
const char *const type,
|
||||
void *const userdata,
|
||||
const char *ns,
|
||||
const char *name,
|
||||
const char *type,
|
||||
void *userdata,
|
||||
int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
@@ -421,7 +435,7 @@ static void _handler_add(xmpp_conn_t *const conn,
|
||||
/* same handler function can process different stanzas and
|
||||
distinguish them according to userdata. */
|
||||
if (item->handler == handler && item->userdata == userdata) {
|
||||
xmpp_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
|
||||
strophe_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -429,47 +443,21 @@ static void _handler_add(xmpp_conn_t *const conn,
|
||||
return;
|
||||
|
||||
/* build new item */
|
||||
item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
item = (xmpp_handlist_t *)strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
memset(item, 0, sizeof(*item));
|
||||
item->user_handler = user_handler;
|
||||
item->handler = handler;
|
||||
item->userdata = userdata;
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
if (ns) {
|
||||
item->u.ns = xmpp_strdup(conn->ctx, ns);
|
||||
if (!item->u.ns) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
item->u.ns = NULL;
|
||||
|
||||
if (name) {
|
||||
item->u.name = xmpp_strdup(conn->ctx, name);
|
||||
if (!item->u.name) {
|
||||
if (item->u.ns)
|
||||
xmpp_free(conn->ctx, item->u.ns);
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
item->u.name = NULL;
|
||||
|
||||
if (type) {
|
||||
item->u.type = xmpp_strdup(conn->ctx, type);
|
||||
if (!item->u.type) {
|
||||
if (item->u.ns)
|
||||
xmpp_free(conn->ctx, item->u.ns);
|
||||
if (item->u.name)
|
||||
xmpp_free(conn->ctx, item->u.name);
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
} else
|
||||
item->u.type = NULL;
|
||||
if (_dup_string(conn->ctx, ns, &item->u.ns))
|
||||
goto error_out;
|
||||
if (_dup_string(conn->ctx, name, &item->u.name))
|
||||
goto error_out;
|
||||
if (_dup_string(conn->ctx, type, &item->u.type))
|
||||
goto error_out;
|
||||
|
||||
/* append to list */
|
||||
if (!conn->handlers)
|
||||
@@ -480,6 +468,11 @@ static void _handler_add(xmpp_conn_t *const conn,
|
||||
tail = tail->next;
|
||||
tail->next = item;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error_out:
|
||||
_free_handlist_item(conn->ctx, item);
|
||||
}
|
||||
|
||||
/** Delete a stanza handler.
|
||||
@@ -489,7 +482,7 @@ static void _handler_add(xmpp_conn_t *const conn,
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler)
|
||||
void xmpp_handler_delete(xmpp_conn_t *conn, xmpp_handler handler)
|
||||
{
|
||||
xmpp_handlist_t *prev, *item;
|
||||
|
||||
@@ -505,13 +498,7 @@ void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler)
|
||||
else
|
||||
conn->handlers = item->next;
|
||||
|
||||
if (item->u.ns)
|
||||
xmpp_free(conn->ctx, item->u.ns);
|
||||
if (item->u.name)
|
||||
xmpp_free(conn->ctx, item->u.name);
|
||||
if (item->u.type)
|
||||
xmpp_free(conn->ctx, item->u.type);
|
||||
xmpp_free(conn->ctx, item);
|
||||
_free_handlist_item(conn->ctx, item);
|
||||
item = prev ? prev->next : conn->handlers;
|
||||
} else {
|
||||
prev = item;
|
||||
@@ -536,10 +523,10 @@ void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler)
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_timed_handler_add(xmpp_conn_t *const conn,
|
||||
void xmpp_timed_handler_add(xmpp_conn_t *conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void *const userdata)
|
||||
unsigned long period,
|
||||
void *userdata)
|
||||
{
|
||||
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
|
||||
userdata, 1);
|
||||
@@ -554,10 +541,10 @@ void xmpp_timed_handler_add(xmpp_conn_t *const conn,
|
||||
* @param period the time in milliseconds between firings
|
||||
* @param userdata an opaque data pointer that will be passed to the handler
|
||||
*/
|
||||
void handler_add_timed(xmpp_conn_t *const conn,
|
||||
void handler_add_timed(xmpp_conn_t *conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void *const userdata)
|
||||
unsigned long period,
|
||||
void *userdata)
|
||||
{
|
||||
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
|
||||
userdata, 0);
|
||||
@@ -579,10 +566,10 @@ void handler_add_timed(xmpp_conn_t *const conn,
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_id_handler_add(xmpp_conn_t *const conn,
|
||||
void xmpp_id_handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const id,
|
||||
void *const userdata)
|
||||
const char *id,
|
||||
void *userdata)
|
||||
{
|
||||
_id_handler_add(conn, handler, id, userdata, 1);
|
||||
}
|
||||
@@ -596,10 +583,10 @@ void xmpp_id_handler_add(xmpp_conn_t *const conn,
|
||||
* @param id a string with the id
|
||||
* @param userdata an opaque data pointer that will be passed to the handler
|
||||
*/
|
||||
void handler_add_id(xmpp_conn_t *const conn,
|
||||
void handler_add_id(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const id,
|
||||
void *const userdata)
|
||||
const char *id,
|
||||
void *userdata)
|
||||
{
|
||||
_id_handler_add(conn, handler, id, userdata, 0);
|
||||
}
|
||||
@@ -626,12 +613,12 @@ void handler_add_id(xmpp_conn_t *const conn,
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_handler_add(xmpp_conn_t *const conn,
|
||||
void xmpp_handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const ns,
|
||||
const char *const name,
|
||||
const char *const type,
|
||||
void *const userdata)
|
||||
const char *ns,
|
||||
const char *name,
|
||||
const char *type,
|
||||
void *userdata)
|
||||
{
|
||||
_handler_add(conn, handler, ns, name, type, userdata, 1);
|
||||
}
|
||||
@@ -647,12 +634,12 @@ void xmpp_handler_add(xmpp_conn_t *const conn,
|
||||
* @param type a string with the 'type' attribute value to match
|
||||
* @param userdata an opaque data pointer that will be passed to the handler
|
||||
*/
|
||||
void handler_add(xmpp_conn_t *const conn,
|
||||
void handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const ns,
|
||||
const char *const name,
|
||||
const char *const type,
|
||||
void *const userdata)
|
||||
const char *ns,
|
||||
const char *name,
|
||||
const char *type,
|
||||
void *userdata)
|
||||
{
|
||||
_handler_add(conn, handler, ns, name, type, userdata, 0);
|
||||
}
|
||||
@@ -666,8 +653,7 @@ void handler_system_delete_all(xmpp_conn_t *conn)
|
||||
{
|
||||
xmpp_handlist_t *item, *next, *head, *head_old;
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
char *key2 = NULL;
|
||||
const char *key, *key2;
|
||||
|
||||
/* TODO unify all kinds of handlers and avoid copy-paste below */
|
||||
|
||||
@@ -676,13 +662,7 @@ void handler_system_delete_all(xmpp_conn_t *conn)
|
||||
if (!item->user_handler) {
|
||||
next = item->next;
|
||||
_handler_item_remove(&conn->handlers, item);
|
||||
if (item->u.ns)
|
||||
xmpp_free(conn->ctx, item->u.ns);
|
||||
if (item->u.name)
|
||||
xmpp_free(conn->ctx, item->u.name);
|
||||
if (item->u.type)
|
||||
xmpp_free(conn->ctx, item->u.type);
|
||||
xmpp_free(conn->ctx, item);
|
||||
_free_handlist_item(conn->ctx, item);
|
||||
item = next;
|
||||
} else
|
||||
item = item->next;
|
||||
@@ -693,7 +673,7 @@ void handler_system_delete_all(xmpp_conn_t *conn)
|
||||
if (!item->user_handler) {
|
||||
next = item->next;
|
||||
_handler_item_remove(&conn->timed_handlers, item);
|
||||
xmpp_free(conn->ctx, item);
|
||||
strophe_free(conn->ctx, item);
|
||||
item = next;
|
||||
} else
|
||||
item = item->next;
|
||||
@@ -708,26 +688,24 @@ void handler_system_delete_all(xmpp_conn_t *conn)
|
||||
if (!item->user_handler) {
|
||||
next = item->next;
|
||||
_handler_item_remove(&head, item);
|
||||
xmpp_free(conn->ctx, item->u.id);
|
||||
xmpp_free(conn->ctx, item);
|
||||
strophe_free(conn->ctx, item->u.id);
|
||||
strophe_free(conn->ctx, item);
|
||||
item = next;
|
||||
} else
|
||||
item = item->next;
|
||||
}
|
||||
if (head != head_old)
|
||||
key2 = xmpp_strdup(conn->ctx, key);
|
||||
/* Hash table implementation is not perfect, so we need to find next
|
||||
key before dropping current one. Otherwise, we will get access to
|
||||
freed memory. */
|
||||
key = hash_iter_next(iter);
|
||||
key2 = hash_iter_next(iter);
|
||||
if (head != head_old) {
|
||||
/* hash_add() replaces value if the key exists */
|
||||
if (head != NULL)
|
||||
hash_add(conn->id_handlers, key2, head);
|
||||
hash_add(conn->id_handlers, key, head);
|
||||
else
|
||||
hash_drop(conn->id_handlers, key2);
|
||||
xmpp_free(conn->ctx, key2);
|
||||
hash_drop(conn->id_handlers, key);
|
||||
}
|
||||
key = key2;
|
||||
}
|
||||
if (iter)
|
||||
hash_iter_release(iter);
|
||||
@@ -762,10 +740,10 @@ void handler_system_delete_all(xmpp_conn_t *conn)
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx,
|
||||
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
|
||||
xmpp_global_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void *const userdata)
|
||||
unsigned long period,
|
||||
void *userdata)
|
||||
{
|
||||
_timed_handler_add(ctx, &ctx->timed_handlers, handler, period, userdata, 1);
|
||||
}
|
||||
@@ -777,7 +755,7 @@ void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx,
|
||||
*
|
||||
* @ingroup Handlers
|
||||
*/
|
||||
void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx,
|
||||
void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
|
||||
xmpp_global_timed_handler handler)
|
||||
{
|
||||
_timed_handler_delete(ctx, &ctx->timed_handlers, handler);
|
||||
|
||||
40
src/hash.c
40
src/hash.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* hash.c
|
||||
** strophe XMPP client library -- hash table implementation
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -46,16 +47,15 @@ struct _hash_iterator_t {
|
||||
};
|
||||
|
||||
/** allocate and initialize a new hash table */
|
||||
hash_t *
|
||||
hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func)
|
||||
hash_t *hash_new(xmpp_ctx_t *ctx, int size, hash_free_func free_func)
|
||||
{
|
||||
hash_t *result = NULL;
|
||||
|
||||
result = xmpp_alloc(ctx, sizeof(hash_t));
|
||||
result = strophe_alloc(ctx, sizeof(hash_t));
|
||||
if (result != NULL) {
|
||||
result->entries = xmpp_alloc(ctx, size * sizeof(hashentry_t *));
|
||||
result->entries = strophe_alloc(ctx, size * sizeof(hashentry_t *));
|
||||
if (result->entries == NULL) {
|
||||
xmpp_free(ctx, result);
|
||||
strophe_free(ctx, result);
|
||||
return NULL;
|
||||
}
|
||||
memset(result->entries, 0, size * sizeof(hashentry_t *));
|
||||
@@ -72,14 +72,14 @@ hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func)
|
||||
}
|
||||
|
||||
/** obtain a new reference to an existing hash table */
|
||||
hash_t *hash_clone(hash_t *const table)
|
||||
hash_t *hash_clone(hash_t *table)
|
||||
{
|
||||
table->ref++;
|
||||
return table;
|
||||
}
|
||||
|
||||
/** release a hash table that is no longer needed */
|
||||
void hash_release(hash_t *const table)
|
||||
void hash_release(hash_t *table)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry, *next;
|
||||
@@ -92,15 +92,15 @@ void hash_release(hash_t *const table)
|
||||
entry = table->entries[i];
|
||||
while (entry != NULL) {
|
||||
next = entry->next;
|
||||
xmpp_free(ctx, entry->key);
|
||||
strophe_free(ctx, entry->key);
|
||||
if (table->free)
|
||||
table->free(ctx, entry->value);
|
||||
xmpp_free(ctx, entry);
|
||||
strophe_free(ctx, entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
xmpp_free(ctx, table->entries);
|
||||
xmpp_free(ctx, table);
|
||||
strophe_free(ctx, table->entries);
|
||||
strophe_free(ctx, table);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ hashentry_t *_hash_entry_find(hash_t *table, const char *key)
|
||||
* each key can appear only once; the value of any
|
||||
* identical key will be replaced
|
||||
*/
|
||||
int hash_add(hash_t *table, const char *const key, void *data)
|
||||
int hash_add(hash_t *table, const char *key, void *data)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry = NULL;
|
||||
@@ -154,12 +154,12 @@ int hash_add(hash_t *table, const char *const key, void *data)
|
||||
|
||||
if (entry == NULL) {
|
||||
/* allocate and fill a new entry */
|
||||
entry = xmpp_alloc(ctx, sizeof(hashentry_t));
|
||||
entry = strophe_alloc(ctx, sizeof(hashentry_t));
|
||||
if (!entry)
|
||||
return -1;
|
||||
entry->key = xmpp_strdup(ctx, key);
|
||||
entry->key = strophe_strdup(ctx, key);
|
||||
if (!entry->key) {
|
||||
xmpp_free(ctx, entry);
|
||||
strophe_free(ctx, entry);
|
||||
return -1;
|
||||
}
|
||||
/* insert ourselves in the linked list */
|
||||
@@ -199,7 +199,7 @@ int hash_drop(hash_t *table, const char *key)
|
||||
/* traverse the linked list looking for the key */
|
||||
if (!strcmp(key, entry->key)) {
|
||||
/* match, remove the entry */
|
||||
xmpp_free(ctx, entry->key);
|
||||
strophe_free(ctx, entry->key);
|
||||
if (table->free)
|
||||
table->free(ctx, entry->value);
|
||||
if (prev == NULL) {
|
||||
@@ -207,7 +207,7 @@ int hash_drop(hash_t *table, const char *key)
|
||||
} else {
|
||||
prev->next = entry->next;
|
||||
}
|
||||
xmpp_free(ctx, entry);
|
||||
strophe_free(ctx, entry);
|
||||
table->num_keys--;
|
||||
return 0;
|
||||
}
|
||||
@@ -229,7 +229,7 @@ hash_iterator_t *hash_iter_new(hash_t *table)
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hash_iterator_t *iter;
|
||||
|
||||
iter = xmpp_alloc(ctx, sizeof(*iter));
|
||||
iter = strophe_alloc(ctx, sizeof(*iter));
|
||||
if (iter != NULL) {
|
||||
iter->ref = 1;
|
||||
iter->table = hash_clone(table);
|
||||
@@ -249,7 +249,7 @@ void hash_iter_release(hash_iterator_t *iter)
|
||||
|
||||
if (iter->ref == 0) { // ref is unsigned!!!
|
||||
hash_release(iter->table);
|
||||
xmpp_free(ctx, iter);
|
||||
strophe_free(ctx, iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
src/hash.h
14
src/hash.h
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* hash.h
|
||||
** strophe XMPP client library -- hash table interface
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -18,23 +19,22 @@
|
||||
|
||||
typedef struct _hash_t hash_t;
|
||||
|
||||
typedef void (*hash_free_func)(const xmpp_ctx_t *const ctx, void *p);
|
||||
typedef void (*hash_free_func)(const xmpp_ctx_t *ctx, void *p);
|
||||
|
||||
/** allocate and initialize a new hash table */
|
||||
hash_t *
|
||||
hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func);
|
||||
hash_t *hash_new(xmpp_ctx_t *ctx, int size, hash_free_func free_func);
|
||||
|
||||
/** allocate a new reference to an existing hash table */
|
||||
hash_t *hash_clone(hash_t *const table);
|
||||
hash_t *hash_clone(hash_t *table);
|
||||
|
||||
/** release a hash table when no longer needed */
|
||||
void hash_release(hash_t *const table);
|
||||
void hash_release(hash_t *table);
|
||||
|
||||
/** add a key, value pair to a hash table.
|
||||
* each key can appear only once; the value of any
|
||||
* identical key will be replaced
|
||||
*/
|
||||
int hash_add(hash_t *table, const char *const key, void *data);
|
||||
int hash_add(hash_t *table, const char *key, void *data);
|
||||
|
||||
/** look up a key in a hash table */
|
||||
void *hash_get(hash_t *table, const char *key);
|
||||
|
||||
88
src/jid.c
88
src/jid.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* jid.c
|
||||
** strophe XMPP client library -- helper functions for parsing JIDs
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -37,8 +38,10 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx,
|
||||
size_t len, nlen, dlen, rlen;
|
||||
|
||||
/* jid must at least have a domain */
|
||||
if (domain == NULL)
|
||||
if (domain == NULL) {
|
||||
strophe_error(ctx, "jid", "domainpart missing.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* accumulate lengths */
|
||||
dlen = strlen(domain);
|
||||
@@ -46,8 +49,28 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx,
|
||||
rlen = (resource) ? strlen(resource) + 1 : 0;
|
||||
len = nlen + dlen + rlen;
|
||||
|
||||
if (dlen > 1023) {
|
||||
strophe_error(ctx, "jid", "domainpart too long.");
|
||||
return NULL;
|
||||
}
|
||||
if (nlen > 1024) {
|
||||
strophe_error(ctx, "jid", "localpart too long.");
|
||||
return NULL;
|
||||
}
|
||||
if (rlen > 1024) {
|
||||
strophe_error(ctx, "jid", "resourcepart too long.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
if (strcspn(node, "\"&'/:<>@") != nlen - 1) {
|
||||
strophe_error(ctx, "jid", "localpart contained invalid character.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* concat components */
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
result = strophe_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
if (node != NULL) {
|
||||
memcpy(result, node, nlen - 1);
|
||||
@@ -77,7 +100,7 @@ char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
|
||||
size_t len;
|
||||
|
||||
len = strcspn(jid, "/");
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
result = strophe_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, jid, len);
|
||||
result[len] = '\0';
|
||||
@@ -96,17 +119,29 @@ char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
|
||||
*/
|
||||
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *dup_jid = strophe_strdup(ctx, jid);
|
||||
char *result = NULL;
|
||||
const char *c;
|
||||
|
||||
c = strchr(jid, '@');
|
||||
/* Apply the same parsing rules from rfc7622 Section 3.2
|
||||
* 1. Strip resource
|
||||
* 2. take part before the '@'
|
||||
*/
|
||||
|
||||
char *resource = strchr(dup_jid, '/');
|
||||
if (resource != NULL) {
|
||||
*resource = '\0';
|
||||
}
|
||||
|
||||
c = strchr(dup_jid, '@');
|
||||
if (c != NULL) {
|
||||
result = xmpp_alloc(ctx, (c - jid) + 1);
|
||||
result = strophe_alloc(ctx, (c - dup_jid) + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, jid, (c - jid));
|
||||
result[c - jid] = '\0';
|
||||
memcpy(result, dup_jid, (c - dup_jid));
|
||||
result[c - dup_jid] = '\0';
|
||||
}
|
||||
}
|
||||
strophe_free(ctx, dup_jid);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -120,24 +155,29 @@ char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
|
||||
*/
|
||||
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result = NULL;
|
||||
const char *c;
|
||||
size_t dlen;
|
||||
char *dup_jid = strophe_strdup(ctx, jid);
|
||||
|
||||
c = strchr(jid, '@');
|
||||
if (c == NULL) {
|
||||
/* no node, assume domain */
|
||||
c = jid;
|
||||
/* rfc7622 Section 3.2
|
||||
* 1. Remove any portion from the first '/' character to the end of the
|
||||
* string (if there is a '/' character present).
|
||||
*/
|
||||
|
||||
char *resource = strchr(dup_jid, '/');
|
||||
if (resource != NULL) {
|
||||
*resource = '\0';
|
||||
}
|
||||
|
||||
/* 2. Remove any portion from the beginning of the string to the first
|
||||
* '@' character (if there is an '@' character present).
|
||||
*/
|
||||
char *at_sign = strchr(dup_jid, '@');
|
||||
char *result = NULL;
|
||||
if (at_sign != NULL) {
|
||||
result = strophe_strdup(ctx, (at_sign + 1));
|
||||
} else {
|
||||
/* advance past the separator */
|
||||
c++;
|
||||
}
|
||||
dlen = strcspn(c, "/"); /* do not include resource */
|
||||
result = xmpp_alloc(ctx, dlen + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, c, dlen);
|
||||
result[dlen] = '\0';
|
||||
result = strophe_strdup(ctx, dup_jid);
|
||||
}
|
||||
strophe_free(ctx, dup_jid);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -155,5 +195,5 @@ char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
|
||||
const char *c;
|
||||
|
||||
c = strchr(jid, '/');
|
||||
return c != NULL ? xmpp_strdup(ctx, c + 1) : NULL;
|
||||
return c != NULL ? strophe_strdup(ctx, c + 1) : NULL;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
#define PUT_32BIT_LSB_FIRST(cp, value) \
|
||||
do { \
|
||||
(cp)[0] = (value)&0xFF; \
|
||||
(cp)[0] = (value) & 0xFF; \
|
||||
(cp)[1] = ((value) >> 8) & 0xFF; \
|
||||
(cp)[2] = ((value) >> 16) & 0xFF; \
|
||||
(cp)[3] = ((value) >> 24) & 0xFF; \
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* ostypes.h
|
||||
** strophe XMPP client library -- type definitions for platforms
|
||||
** without stdint.h
|
||||
@@ -7,7 +8,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
|
||||
12
src/parser.h
12
src/parser.h
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* parser.h
|
||||
** strophe XMPP client library -- parser structures and functions
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -20,12 +21,9 @@
|
||||
|
||||
typedef struct _parser_t parser_t;
|
||||
|
||||
typedef void (*parser_start_callback)(char *name,
|
||||
char **attrs,
|
||||
void *const userdata);
|
||||
typedef void (*parser_end_callback)(char *name, void *const userdata);
|
||||
typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza,
|
||||
void *const userdata);
|
||||
typedef void (*parser_start_callback)(char *name, char **attrs, void *userdata);
|
||||
typedef void (*parser_end_callback)(char *name, void *userdata);
|
||||
typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza, void *userdata);
|
||||
|
||||
parser_t *parser_new(xmpp_ctx_t *ctx,
|
||||
parser_start_callback startcb,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* parser.c
|
||||
** strophe XMPP client library -- xml parser handlers and utility functions
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -60,7 +61,7 @@ static xmpp_ctx_t *mem_ctx = NULL;
|
||||
static void *parser_mem_malloc(size_t size)
|
||||
{
|
||||
if (mem_ctx != NULL)
|
||||
return xmpp_alloc(mem_ctx, size);
|
||||
return strophe_alloc(mem_ctx, size);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@@ -68,7 +69,7 @@ static void *parser_mem_malloc(size_t size)
|
||||
static void *parser_mem_realloc(void *ptr, size_t size)
|
||||
{
|
||||
if (mem_ctx != NULL)
|
||||
return xmpp_realloc(mem_ctx, ptr, size);
|
||||
return strophe_realloc(mem_ctx, ptr, size);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@@ -76,7 +77,7 @@ static void *parser_mem_realloc(void *ptr, size_t size)
|
||||
static void parser_mem_free(void *ptr)
|
||||
{
|
||||
if (mem_ctx != NULL)
|
||||
xmpp_free(mem_ctx, ptr);
|
||||
strophe_free(mem_ctx, ptr);
|
||||
}
|
||||
|
||||
static const XML_Memory_Handling_Suite parser_mem_suite = {
|
||||
@@ -95,11 +96,11 @@ static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname)
|
||||
|
||||
c = strchr(nsname, namespace_sep);
|
||||
if (c == NULL)
|
||||
return xmpp_strdup(ctx, nsname);
|
||||
return strophe_strdup(ctx, nsname);
|
||||
|
||||
c++;
|
||||
len = strlen(c);
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
result = strophe_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, c, len);
|
||||
result[len] = '\0';
|
||||
@@ -116,7 +117,7 @@ static char *_xml_namespace(xmpp_ctx_t *ctx, const char *nsname)
|
||||
|
||||
c = strchr(nsname, namespace_sep);
|
||||
if (c != NULL) {
|
||||
result = xmpp_alloc(ctx, (c - nsname) + 1);
|
||||
result = strophe_alloc(ctx, (c - nsname) + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, nsname, (c - nsname));
|
||||
result[c - nsname] = '\0';
|
||||
@@ -138,7 +139,7 @@ static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
|
||||
/* namespaced attributes aren't used in xmpp, discard namespace */
|
||||
attr = _xml_name(stanza->ctx, attrs[i]);
|
||||
xmpp_stanza_set_attribute(stanza, attr, attrs[i + 1]);
|
||||
xmpp_free(stanza->ctx, attr);
|
||||
strophe_free(stanza->ctx, attr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,10 +153,9 @@ static void complete_inner_text(parser_t *parser)
|
||||
/* FIXME: disconnect on allocation error */
|
||||
if (stanza) {
|
||||
xmpp_stanza_set_text(stanza, parser->inner_text);
|
||||
xmpp_stanza_add_child(parser->stanza, stanza);
|
||||
xmpp_stanza_release(stanza);
|
||||
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
|
||||
}
|
||||
xmpp_free(parser->ctx, parser->inner_text);
|
||||
strophe_free(parser->ctx, parser->inner_text);
|
||||
parser->inner_text = NULL;
|
||||
parser->inner_text_size = 0;
|
||||
parser->inner_text_used = 0;
|
||||
@@ -181,7 +181,8 @@ _start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
|
||||
if (!parser->stanza && parser->depth != 1) {
|
||||
/* something terrible happened */
|
||||
/* FIXME: shutdown disconnect */
|
||||
xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
|
||||
strophe_error(parser->ctx, "parser",
|
||||
"oops, where did our stanza go?");
|
||||
} else {
|
||||
child = xmpp_stanza_new(parser->ctx);
|
||||
if (!child) {
|
||||
@@ -194,17 +195,16 @@ _start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
|
||||
|
||||
if (parser->stanza != NULL) {
|
||||
complete_inner_text(parser);
|
||||
xmpp_stanza_add_child(parser->stanza, child);
|
||||
xmpp_stanza_release(child);
|
||||
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
|
||||
}
|
||||
parser->stanza = child;
|
||||
}
|
||||
}
|
||||
|
||||
if (ns)
|
||||
xmpp_free(parser->ctx, ns);
|
||||
strophe_free(parser->ctx, ns);
|
||||
if (name)
|
||||
xmpp_free(parser->ctx, name);
|
||||
strophe_free(parser->ctx, name);
|
||||
|
||||
parser->depth++;
|
||||
}
|
||||
@@ -246,10 +246,10 @@ static void _characters(void *userdata, const XML_Char *s, int len)
|
||||
if (parser->inner_text_used + len >= parser->inner_text_size) {
|
||||
parser->inner_text_size =
|
||||
parser->inner_text_used + len + 1 + INNER_TEXT_PADDING;
|
||||
p = xmpp_realloc(parser->ctx, parser->inner_text,
|
||||
parser->inner_text_size);
|
||||
p = strophe_realloc(parser->ctx, parser->inner_text,
|
||||
parser->inner_text_size);
|
||||
if (p == NULL) {
|
||||
xmpp_free(parser->ctx, parser->inner_text);
|
||||
strophe_free(parser->ctx, parser->inner_text);
|
||||
parser->inner_text = NULL;
|
||||
parser->inner_text_used = 0;
|
||||
parser->inner_text_size = 0;
|
||||
@@ -270,7 +270,7 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
||||
{
|
||||
parser_t *parser;
|
||||
|
||||
parser = xmpp_alloc(ctx, sizeof(parser_t));
|
||||
parser = strophe_alloc(ctx, sizeof(parser_t));
|
||||
if (parser != NULL) {
|
||||
parser->ctx = ctx;
|
||||
parser->expat = NULL;
|
||||
@@ -295,18 +295,32 @@ char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
|
||||
return _xml_name(ctx, nsname);
|
||||
}
|
||||
|
||||
static void _free_parent_stanza(xmpp_stanza_t *stanza)
|
||||
{
|
||||
xmpp_stanza_t *parent;
|
||||
|
||||
for (parent = stanza; parent->parent != NULL; parent = parent->parent)
|
||||
;
|
||||
xmpp_stanza_release(parent);
|
||||
}
|
||||
|
||||
/* free a parser */
|
||||
void parser_free(parser_t *parser)
|
||||
{
|
||||
if (parser->expat)
|
||||
XML_ParserFree(parser->expat);
|
||||
|
||||
if (parser->stanza) {
|
||||
_free_parent_stanza(parser->stanza);
|
||||
parser->stanza = NULL;
|
||||
}
|
||||
|
||||
if (parser->inner_text) {
|
||||
xmpp_free(parser->ctx, parser->inner_text);
|
||||
strophe_free(parser->ctx, parser->inner_text);
|
||||
parser->inner_text = NULL;
|
||||
}
|
||||
|
||||
xmpp_free(parser->ctx, parser);
|
||||
strophe_free(parser->ctx, parser);
|
||||
}
|
||||
|
||||
/* shuts down and restarts XML parser. true on success */
|
||||
@@ -330,12 +344,12 @@ int parser_reset(parser_t *parser)
|
||||
}
|
||||
|
||||
if (parser->stanza) {
|
||||
xmpp_stanza_release(parser->stanza);
|
||||
_free_parent_stanza(parser->stanza);
|
||||
parser->stanza = NULL;
|
||||
}
|
||||
|
||||
if (parser->inner_text) {
|
||||
xmpp_free(parser->ctx, parser->inner_text);
|
||||
strophe_free(parser->ctx, parser->inner_text);
|
||||
parser->inner_text = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* parser.c
|
||||
** strophe XMPP client library -- xml parser handlers and utility functions
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -48,12 +49,12 @@ _set_attributes(xmpp_stanza_t *stanza, int nattrs, const xmlChar **attrs)
|
||||
/* SAX2 uses array of localname/prefix/uri/value_begin/value_end */
|
||||
for (i = 0; i < nattrs * 5; i += 5) {
|
||||
len = attrs[i + 4] - attrs[i + 3];
|
||||
value = xmpp_alloc(stanza->ctx, len + 1);
|
||||
value = strophe_alloc(stanza->ctx, len + 1);
|
||||
if (value) {
|
||||
memcpy(value, attrs[i + 3], len);
|
||||
value[len] = '\0';
|
||||
xmpp_stanza_set_attribute(stanza, (const char *)attrs[i], value);
|
||||
xmpp_free(stanza->ctx, value);
|
||||
strophe_free(stanza->ctx, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,7 +71,7 @@ _convert_attrs(parser_t *parser, int nattrs, const xmlChar **attrs)
|
||||
if (!attrs)
|
||||
return NULL;
|
||||
|
||||
ret = xmpp_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *));
|
||||
ret = strophe_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
memset(ret, 0, (nattrs + 1) * 2 * sizeof(char *));
|
||||
@@ -80,11 +81,11 @@ _convert_attrs(parser_t *parser, int nattrs, const xmlChar **attrs)
|
||||
o = c * 2;
|
||||
|
||||
len = attrs[i + 4] - attrs[i + 3];
|
||||
value = xmpp_alloc(parser->ctx, len + 1);
|
||||
value = strophe_alloc(parser->ctx, len + 1);
|
||||
if (value) {
|
||||
memcpy(value, attrs[i + 3], len);
|
||||
value[len] = '\0';
|
||||
ret[o] = xmpp_strdup(parser->ctx, (char *)attrs[i]);
|
||||
ret[o] = strophe_strdup(parser->ctx, (char *)attrs[i]);
|
||||
ret[o + 1] = value;
|
||||
}
|
||||
}
|
||||
@@ -101,12 +102,12 @@ static void _free_cbattrs(parser_t *parser, char **attrs)
|
||||
|
||||
for (i = 0; attrs[i]; i += 2) {
|
||||
if (attrs[i])
|
||||
xmpp_free(parser->ctx, attrs[i]);
|
||||
strophe_free(parser->ctx, attrs[i]);
|
||||
if (attrs[i + 1])
|
||||
xmpp_free(parser->ctx, attrs[i + 1]);
|
||||
strophe_free(parser->ctx, attrs[i + 1]);
|
||||
}
|
||||
|
||||
xmpp_free(parser->ctx, attrs);
|
||||
strophe_free(parser->ctx, attrs);
|
||||
}
|
||||
|
||||
static void _start_element(void *userdata,
|
||||
@@ -140,7 +141,8 @@ static void _start_element(void *userdata,
|
||||
if (!parser->stanza && parser->depth != 1) {
|
||||
/* something terrible happened */
|
||||
/* FIXME: we should probably trigger a disconnect */
|
||||
xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
|
||||
strophe_error(parser->ctx, "parser",
|
||||
"oops, where did our stanza go?");
|
||||
} else if (!parser->stanza) {
|
||||
/* starting a new toplevel stanza */
|
||||
parser->stanza = xmpp_stanza_new(parser->ctx);
|
||||
@@ -163,10 +165,7 @@ static void _start_element(void *userdata,
|
||||
xmpp_stanza_set_ns(child, (char *)uri);
|
||||
|
||||
/* add child to parent */
|
||||
xmpp_stanza_add_child(parser->stanza, child);
|
||||
|
||||
/* the child is owned by the toplevel stanza now */
|
||||
xmpp_stanza_release(child);
|
||||
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
|
||||
|
||||
/* make child the current stanza */
|
||||
parser->stanza = child;
|
||||
@@ -222,8 +221,7 @@ static void _characters(void *userdata, const xmlChar *chr, int len)
|
||||
}
|
||||
xmpp_stanza_set_text_with_size(stanza, (char *)chr, len);
|
||||
|
||||
xmpp_stanza_add_child(parser->stanza, stanza);
|
||||
xmpp_stanza_release(stanza);
|
||||
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
|
||||
}
|
||||
|
||||
/* create a new parser */
|
||||
@@ -235,7 +233,7 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
||||
{
|
||||
parser_t *parser;
|
||||
|
||||
parser = xmpp_alloc(ctx, sizeof(parser_t));
|
||||
parser = strophe_alloc(ctx, sizeof(parser_t));
|
||||
if (parser != NULL) {
|
||||
parser->ctx = ctx;
|
||||
parser->xmlctx = NULL;
|
||||
@@ -259,7 +257,16 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
||||
|
||||
char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
|
||||
{
|
||||
return xmpp_strdup(ctx, nsname);
|
||||
return strophe_strdup(ctx, nsname);
|
||||
}
|
||||
|
||||
static void _free_parent_stanza(xmpp_stanza_t *stanza)
|
||||
{
|
||||
xmpp_stanza_t *parent;
|
||||
|
||||
for (parent = stanza; parent->parent != NULL; parent = parent->parent)
|
||||
;
|
||||
xmpp_stanza_release(parent);
|
||||
}
|
||||
|
||||
/* free a parser */
|
||||
@@ -268,8 +275,8 @@ void parser_free(parser_t *parser)
|
||||
if (parser->xmlctx)
|
||||
xmlFreeParserCtxt(parser->xmlctx);
|
||||
if (parser->stanza)
|
||||
xmpp_stanza_release(parser->stanza);
|
||||
xmpp_free(parser->ctx, parser);
|
||||
_free_parent_stanza(parser->stanza);
|
||||
strophe_free(parser->ctx, parser);
|
||||
}
|
||||
|
||||
/* shuts down and restarts XML parser. true on success */
|
||||
@@ -278,7 +285,7 @@ int parser_reset(parser_t *parser)
|
||||
if (parser->xmlctx)
|
||||
xmlFreeParserCtxt(parser->xmlctx);
|
||||
if (parser->stanza)
|
||||
xmpp_stanza_release(parser->stanza);
|
||||
_free_parent_stanza(parser->stanza);
|
||||
|
||||
parser->stanza = NULL;
|
||||
parser->depth = 0;
|
||||
|
||||
96
src/rand.c
96
src/rand.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* rand.c
|
||||
* strophe XMPP client library -- pseudo-random number generator
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -23,12 +24,24 @@
|
||||
#include <string.h> /* memeset */
|
||||
#include <time.h> /* clock, time */
|
||||
|
||||
#include "common.h" /* xmpp_alloc, xmpp_free */
|
||||
#include "ostypes.h" /* uint8_t, uint32_t, size_t */
|
||||
#include "sha1.h"
|
||||
#include "snprintf.h" /* xmpp_snprintf */
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "rand.h" /* xmpp_rand_t */
|
||||
#if !defined(DONT_USE_GETRANDOM) && defined(__linux__) && \
|
||||
defined(__GLIBC_PREREQ)
|
||||
#if __GLIBC_PREREQ(2, 25)
|
||||
#define USE_GETRANDOM
|
||||
#include <sys/random.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "common.h" /* strophe_alloc, strophe_free */
|
||||
#include "ostypes.h" /* uint8_t, uint32_t, size_t */
|
||||
|
||||
#ifndef USE_GETRANDOM
|
||||
#include "sha1.h"
|
||||
|
||||
#define outlen SHA1_DIGEST_SIZE
|
||||
#define seedlen (440 / 8)
|
||||
@@ -55,9 +68,9 @@ struct _xmpp_rand_t {
|
||||
};
|
||||
|
||||
/* returns smallest number mupliple of y that not less than x */
|
||||
#define round_up(x, y) (((x) + (y)-1) / (y) * (y))
|
||||
#define round_up(x, y) (((x) + (y) - 1) / (y) * (y))
|
||||
/* returns smallest integer number that not less than x/y */
|
||||
#define div_round_up(x, y) (((x) + (y)-1) / (y))
|
||||
#define div_round_up(x, y) (((x) + (y) - 1) / (y))
|
||||
|
||||
/* adds two arrays as numbers in big-endian representation and stores
|
||||
* result in the first one.
|
||||
@@ -261,7 +274,7 @@ static void xmpp_rand_reseed(xmpp_rand_t *rand)
|
||||
|
||||
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_rand_t *out = xmpp_alloc(ctx, sizeof(*out));
|
||||
xmpp_rand_t *out = strophe_alloc(ctx, sizeof(*out));
|
||||
if (out != NULL) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
}
|
||||
@@ -270,21 +283,72 @@ xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
|
||||
|
||||
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
|
||||
{
|
||||
xmpp_free(ctx, rand);
|
||||
strophe_free(ctx, rand);
|
||||
}
|
||||
|
||||
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output, len);
|
||||
if (rc == RESEED_NEEDED) {
|
||||
xmpp_rand_reseed(rand);
|
||||
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output, len);
|
||||
assert(rc == 0);
|
||||
size_t gen, tot = 0;
|
||||
while (tot < len) {
|
||||
gen = len - tot;
|
||||
if (gen > GENERATE_MAX)
|
||||
gen = GENERATE_MAX;
|
||||
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
|
||||
if (rc == RESEED_NEEDED) {
|
||||
xmpp_rand_reseed(rand);
|
||||
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
|
||||
assert(rc == 0);
|
||||
}
|
||||
tot += gen;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int _read_getrandom(void *p, size_t n)
|
||||
{
|
||||
unsigned char *q = (unsigned char *)p;
|
||||
while (n > 0u) {
|
||||
ssize_t ret = getrandom(q, n, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
q += ret;
|
||||
n -= (size_t)ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct _xmpp_rand_t {
|
||||
char nothing;
|
||||
};
|
||||
|
||||
static xmpp_rand_t _xmpp_rand;
|
||||
|
||||
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
return &_xmpp_rand;
|
||||
}
|
||||
|
||||
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
assert(rand == &_xmpp_rand);
|
||||
}
|
||||
|
||||
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
|
||||
{
|
||||
assert(rand == &_xmpp_rand);
|
||||
assert(_read_getrandom(output, len) == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int xmpp_rand(xmpp_rand_t *rand)
|
||||
{
|
||||
int result;
|
||||
|
||||
65
src/rand.h
65
src/rand.h
@@ -1,65 +0,0 @@
|
||||
/* rand.h
|
||||
* strophe XMPP client library -- pseudo-random number generator
|
||||
*
|
||||
* Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||
*
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Pseudo-random number generator.
|
||||
*/
|
||||
|
||||
#ifndef __LIBSTROPHE_RAND_H__
|
||||
#define __LIBSTROPHE_RAND_H__
|
||||
|
||||
#include <stddef.h> /* size_t */
|
||||
#include "strophe.h" /* xmpp_ctx_t */
|
||||
|
||||
typedef struct _xmpp_rand_t xmpp_rand_t;
|
||||
|
||||
/** Create new xmpp_rand_t object.
|
||||
*
|
||||
* @param ctx A Strophe context object
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
|
||||
/** Destroy an xmpp_rand_t object.
|
||||
*
|
||||
* @param ctx A Strophe context object
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
|
||||
|
||||
/** Generate random integer.
|
||||
* Analogue of rand(3).
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
int xmpp_rand(xmpp_rand_t *rand);
|
||||
|
||||
/** Generate random bytes.
|
||||
* Generates len bytes and stores them to the output buffer.
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
|
||||
|
||||
/** Generate a nonce that is printable randomized string.
|
||||
* This function doesn't allocate memory and doesn't fail.
|
||||
*
|
||||
* @param output A buffer where a NULL-terminated string will be placed.
|
||||
* The string will contain len-1 printable symbols.
|
||||
* @param len Number of bytes reserved for the output string, including
|
||||
* end of line '\0'.
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
|
||||
|
||||
#endif /* __LIBSTROPHE_RAND_H__ */
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* resolver.c
|
||||
* strophe XMPP client library -- DNS resolver
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -91,6 +92,25 @@ void resolver_shutdown(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
resolver_srv_rr_t *resolver_srv_rr_new(xmpp_ctx_t *ctx,
|
||||
const char *host,
|
||||
unsigned short port,
|
||||
unsigned short prio,
|
||||
unsigned short weight)
|
||||
{
|
||||
resolver_srv_rr_t *rr = strophe_alloc(ctx, sizeof(*rr));
|
||||
if (rr) {
|
||||
memset(rr, 0, sizeof(*rr));
|
||||
rr->port = port;
|
||||
rr->priority = prio;
|
||||
rr->weight = weight;
|
||||
if (host) {
|
||||
snprintf(rr->target, sizeof(rr->target), "%s", host);
|
||||
}
|
||||
}
|
||||
return rr;
|
||||
}
|
||||
|
||||
static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
|
||||
{
|
||||
resolver_srv_rr_t *rr_head;
|
||||
@@ -158,6 +178,10 @@ int resolver_srv_lookup_buf(xmpp_ctx_t *ctx,
|
||||
set = resolver_ares_srv_lookup_buf(ctx, buf, len, srv_rr_list);
|
||||
#else
|
||||
set = resolver_raw_srv_lookup_buf(ctx, buf, len, srv_rr_list);
|
||||
if (set != XMPP_DOMAIN_FOUND && *srv_rr_list != NULL) {
|
||||
resolver_srv_free(ctx, *srv_rr_list);
|
||||
*srv_rr_list = NULL;
|
||||
}
|
||||
#endif
|
||||
resolver_srv_list_sort(srv_rr_list);
|
||||
|
||||
@@ -179,8 +203,8 @@ int resolver_srv_lookup(xmpp_ctx_t *ctx,
|
||||
(void)buf;
|
||||
(void)len;
|
||||
|
||||
xmpp_snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service, proto,
|
||||
domain);
|
||||
strophe_snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service,
|
||||
proto, domain);
|
||||
|
||||
*srv_rr_list = NULL;
|
||||
|
||||
@@ -196,7 +220,7 @@ int resolver_srv_lookup(xmpp_ctx_t *ctx,
|
||||
return set;
|
||||
#endif /* _WIN32 */
|
||||
|
||||
buf = xmpp_alloc(ctx, RESOLVER_BUF_MAX);
|
||||
buf = strophe_alloc(ctx, RESOLVER_BUF_MAX);
|
||||
if (buf == NULL)
|
||||
return XMPP_DOMAIN_NOT_FOUND;
|
||||
|
||||
@@ -210,7 +234,7 @@ int resolver_srv_lookup(xmpp_ctx_t *ctx,
|
||||
if (len > 0)
|
||||
set = resolver_srv_lookup_buf(ctx, buf, (size_t)len, srv_rr_list);
|
||||
|
||||
xmpp_free(ctx, buf);
|
||||
strophe_free(ctx, buf);
|
||||
|
||||
#endif /* HAVE_CARES */
|
||||
|
||||
@@ -223,7 +247,7 @@ void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list)
|
||||
|
||||
while (srv_rr_list != NULL) {
|
||||
rr = srv_rr_list->next;
|
||||
xmpp_free(ctx, srv_rr_list);
|
||||
strophe_free(ctx, srv_rr_list);
|
||||
srv_rr_list = rr;
|
||||
}
|
||||
}
|
||||
@@ -321,6 +345,9 @@ static unsigned message_name_get(const unsigned char *buf,
|
||||
if (i >= buf_len)
|
||||
return 0;
|
||||
pointer = (label_len & 0x3f) << 8 | buf[i++];
|
||||
/* Prevent infinite looping */
|
||||
if (pointer >= buf_offset)
|
||||
return 0;
|
||||
if (name != NULL && name_len >= name_max && name_max > 0) {
|
||||
/* We have filled the name buffer. Don't pass it recursively. */
|
||||
name[name_max - 1] = '\0';
|
||||
@@ -431,17 +458,19 @@ static int resolver_raw_srv_lookup_buf(xmpp_ctx_t *ctx,
|
||||
rdlength = xmpp_ntohs_ptr(&buf[j + 8]);
|
||||
j += 10;
|
||||
if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) {
|
||||
rr = xmpp_alloc(ctx, sizeof(*rr));
|
||||
rr->next = *srv_rr_list;
|
||||
rr->priority = xmpp_ntohs_ptr(&buf[j]);
|
||||
rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
|
||||
rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
|
||||
name_len = message_name_get(buf, len, j + 6, rr->target,
|
||||
sizeof(rr->target));
|
||||
if (name_len > 0)
|
||||
*srv_rr_list = rr;
|
||||
else
|
||||
xmpp_free(ctx, rr); /* skip broken record */
|
||||
rr = resolver_srv_rr_new(ctx, NULL, 0, 0, 0);
|
||||
if (rr) {
|
||||
rr->next = *srv_rr_list;
|
||||
rr->priority = xmpp_ntohs_ptr(&buf[j]);
|
||||
rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
|
||||
rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
|
||||
name_len = message_name_get(buf, len, j + 6, rr->target,
|
||||
sizeof(rr->target));
|
||||
if (name_len > 0)
|
||||
*srv_rr_list = rr;
|
||||
else
|
||||
strophe_free(ctx, rr); /* skip broken record */
|
||||
}
|
||||
}
|
||||
j += rdlength;
|
||||
}
|
||||
@@ -480,7 +509,7 @@ static int resolver_ares_srv_lookup_buf(xmpp_ctx_t *ctx,
|
||||
|
||||
item = srv;
|
||||
while (item != NULL) {
|
||||
rr = xmpp_alloc(ctx, sizeof(*rr));
|
||||
rr = strophe_alloc(ctx, sizeof(*rr));
|
||||
if (rr == NULL)
|
||||
break;
|
||||
rr->next = *srv_rr_list;
|
||||
@@ -601,7 +630,7 @@ static void netbuf_add_16bitnum(unsigned char *buf,
|
||||
|
||||
/* assuming big endian */
|
||||
*p++ = (num >> 8) & 0xff;
|
||||
*p++ = (num)&0xff;
|
||||
*p++ = (num) & 0xff;
|
||||
|
||||
*offset += 2;
|
||||
}
|
||||
@@ -705,15 +734,15 @@ static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx,
|
||||
|
||||
while (current) {
|
||||
if (current->wType == DNS_TYPE_SRV) {
|
||||
rr = xmpp_alloc(ctx, sizeof(*rr));
|
||||
rr = strophe_alloc(ctx, sizeof(*rr));
|
||||
if (rr == NULL)
|
||||
break;
|
||||
rr->next = *srv_rr_list;
|
||||
rr->port = current->Data.Srv.wPort;
|
||||
rr->priority = current->Data.Srv.wPriority;
|
||||
rr->weight = current->Data.Srv.wWeight;
|
||||
xmpp_snprintf(rr->target, sizeof(rr->target), "%s",
|
||||
current->Data.Srv.pNameTarget);
|
||||
strophe_snprintf(rr->target, sizeof(rr->target), "%s",
|
||||
current->Data.Srv.pNameTarget);
|
||||
*srv_rr_list = rr;
|
||||
}
|
||||
current = current->pNext;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* resolver.h
|
||||
* strophe XMPP client library -- DNS resolver
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -36,6 +37,12 @@ typedef struct resolver_srv_rr_struc {
|
||||
void resolver_initialize(void);
|
||||
void resolver_shutdown(void);
|
||||
|
||||
resolver_srv_rr_t *resolver_srv_rr_new(xmpp_ctx_t *ctx,
|
||||
const char *host,
|
||||
unsigned short port,
|
||||
unsigned short prio,
|
||||
unsigned short weight);
|
||||
|
||||
/** Perform lookup for RFC1035 message format.
|
||||
* This function allocates all elements.
|
||||
*
|
||||
|
||||
109
src/sasl.c
109
src/sasl.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* sasl.c
|
||||
** strophe XMPP client library -- SASL authentication helpers
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -22,7 +23,6 @@
|
||||
#include "sasl.h"
|
||||
#include "md5.h"
|
||||
#include "scram.h"
|
||||
#include "rand.h"
|
||||
#include "util.h"
|
||||
|
||||
/* strtok_s() has appeared in visual studio 2005.
|
||||
@@ -49,14 +49,14 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password)
|
||||
idlen = strlen(authid);
|
||||
passlen = strlen(password);
|
||||
msglen = 2 + idlen + passlen;
|
||||
msg = xmpp_alloc(ctx, msglen);
|
||||
msg = strophe_alloc(ctx, msglen);
|
||||
if (msg != NULL) {
|
||||
msg[0] = '\0';
|
||||
memcpy(msg + 1, authid, idlen);
|
||||
msg[1 + idlen] = '\0';
|
||||
memcpy(msg + 1 + idlen + 1, password, passlen);
|
||||
result = xmpp_base64_encode(ctx, (unsigned char *)msg, msglen);
|
||||
xmpp_free(ctx, msg);
|
||||
strophe_free(ctx, msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -65,11 +65,11 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password)
|
||||
/** helpers for digest auth */
|
||||
|
||||
/* create a new, null-terminated string from a substring */
|
||||
static char *_make_string(xmpp_ctx_t *ctx, const char *s, const unsigned len)
|
||||
static char *_make_string(xmpp_ctx_t *ctx, const char *s, unsigned len)
|
||||
{
|
||||
char *result;
|
||||
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
result = strophe_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, s, len);
|
||||
result[len] = '\0';
|
||||
@@ -83,7 +83,7 @@ static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
|
||||
char *result;
|
||||
size_t len = strlen(s);
|
||||
|
||||
result = xmpp_alloc(ctx, len + 3);
|
||||
result = strophe_alloc(ctx, len + 3);
|
||||
if (result != NULL) {
|
||||
result[0] = '"';
|
||||
memcpy(result + 1, s, len);
|
||||
@@ -103,11 +103,11 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
||||
|
||||
text = (unsigned char *)xmpp_base64_decode_str(ctx, msg, strlen(msg));
|
||||
if (text == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
||||
strophe_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = hash_new(ctx, 10, xmpp_free);
|
||||
result = hash_new(ctx, 10, strophe_free);
|
||||
if (result != NULL) {
|
||||
s = text;
|
||||
while (*s != '\0') {
|
||||
@@ -145,16 +145,16 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
||||
s = t;
|
||||
}
|
||||
if (value == NULL) {
|
||||
xmpp_free(ctx, key);
|
||||
strophe_free(ctx, key);
|
||||
break;
|
||||
}
|
||||
/* TODO: check for collisions per spec */
|
||||
hash_add(result, key, value);
|
||||
/* hash table now owns the value, free the key */
|
||||
xmpp_free(ctx, key);
|
||||
strophe_free(ctx, key);
|
||||
}
|
||||
}
|
||||
xmpp_free(ctx, text);
|
||||
strophe_free(ctx, text);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -182,7 +182,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
|
||||
|
||||
/* allocate a zero-length string if necessary */
|
||||
if (buf == NULL) {
|
||||
buf = xmpp_alloc(ctx, 1);
|
||||
buf = strophe_alloc(ctx, 1);
|
||||
buf[0] = '\0';
|
||||
}
|
||||
if (buf == NULL)
|
||||
@@ -192,7 +192,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
|
||||
olen = strlen(buf);
|
||||
value = hash_get(table, key);
|
||||
if (value == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
||||
strophe_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
||||
value = "";
|
||||
}
|
||||
if (quote) {
|
||||
@@ -205,7 +205,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
|
||||
keylen = strlen(key);
|
||||
valuelen = strlen(qvalue);
|
||||
nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
|
||||
buf = xmpp_realloc(ctx, buf, olen + nlen);
|
||||
buf = strophe_realloc(ctx, buf, olen + nlen);
|
||||
|
||||
if (buf != NULL) {
|
||||
c = buf + olen;
|
||||
@@ -220,7 +220,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
|
||||
}
|
||||
|
||||
if (quote)
|
||||
xmpp_free(ctx, (char *)qvalue);
|
||||
strophe_free(ctx, (char *)qvalue);
|
||||
|
||||
return buf;
|
||||
}
|
||||
@@ -255,7 +255,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||
/* parse the challenge */
|
||||
table = _parse_digest_challenge(ctx, challenge);
|
||||
if (table == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
|
||||
strophe_error(ctx, "SASL", "couldn't parse digest challenge");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -266,18 +266,18 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||
server */
|
||||
realm = hash_get(table, "realm");
|
||||
if (realm == NULL || strlen(realm) == 0) {
|
||||
hash_add(table, "realm", xmpp_strdup(ctx, domain));
|
||||
hash_add(table, "realm", strophe_strdup(ctx, domain));
|
||||
realm = hash_get(table, "realm");
|
||||
}
|
||||
|
||||
/* add our response fields */
|
||||
hash_add(table, "username", xmpp_strdup(ctx, node));
|
||||
hash_add(table, "username", strophe_strdup(ctx, node));
|
||||
xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce));
|
||||
hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce));
|
||||
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
|
||||
hash_add(table, "cnonce", strophe_strdup(ctx, cnonce));
|
||||
hash_add(table, "nc", strophe_strdup(ctx, "00000001"));
|
||||
if (hash_get(table, "qop") == NULL)
|
||||
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
|
||||
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
|
||||
hash_add(table, "qop", strophe_strdup(ctx, "auth"));
|
||||
value = strophe_alloc(ctx, 5 + strlen(domain) + 1);
|
||||
memcpy(value, "xmpp/", 5);
|
||||
memcpy(value + 5, domain, strlen(domain));
|
||||
value[5 + strlen(domain)] = '\0';
|
||||
@@ -343,7 +343,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||
MD5Update(&MD5, (unsigned char *)hex, 32);
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
response = xmpp_alloc(ctx, 32 + 1);
|
||||
response = strophe_alloc(ctx, 32 + 1);
|
||||
_digest_to_hex((char *)digest, hex);
|
||||
memcpy(response, hex, 32);
|
||||
response[32] = '\0';
|
||||
@@ -361,13 +361,13 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||
result = _add_key(ctx, table, "response", result, 0);
|
||||
result = _add_key(ctx, table, "charset", result, 0);
|
||||
|
||||
xmpp_free(ctx, node);
|
||||
xmpp_free(ctx, domain);
|
||||
strophe_free(ctx, node);
|
||||
strophe_free(ctx, domain);
|
||||
hash_release(table); /* also frees value strings */
|
||||
|
||||
/* reuse response for the base64 encode of our result */
|
||||
response = xmpp_base64_encode(ctx, (unsigned char *)result, strlen(result));
|
||||
xmpp_free(ctx, result);
|
||||
strophe_free(ctx, result);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -375,6 +375,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||
/** generate auth response string for the SASL SCRAM mechanism */
|
||||
char *sasl_scram(xmpp_ctx_t *ctx,
|
||||
const struct hash_alg *alg,
|
||||
const char *channel_binding,
|
||||
const char *challenge,
|
||||
const char *first_bare,
|
||||
const char *jid,
|
||||
@@ -398,10 +399,11 @@ char *sasl_scram(xmpp_ctx_t *ctx,
|
||||
char *result = NULL;
|
||||
size_t response_len;
|
||||
size_t auth_len;
|
||||
int l;
|
||||
|
||||
UNUSED(jid);
|
||||
|
||||
tmp = xmpp_strdup(ctx, challenge);
|
||||
tmp = strophe_strdup(ctx, challenge);
|
||||
if (!tmp) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -428,56 +430,63 @@ char *sasl_scram(xmpp_ctx_t *ctx,
|
||||
}
|
||||
ival = strtol(i, &saveptr, 10);
|
||||
|
||||
auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge);
|
||||
auth = xmpp_alloc(ctx, auth_len);
|
||||
if (!auth) {
|
||||
/* "c=<channel_binding>," + r + ",p=" + sign_b64 + '\0' */
|
||||
response_len = 3 + strlen(channel_binding) + strlen(r) + 3 +
|
||||
((alg->digest_size + 2) / 3 * 4) + 1;
|
||||
response = strophe_alloc(ctx, response_len);
|
||||
if (!response) {
|
||||
goto out_sval;
|
||||
}
|
||||
|
||||
/* "c=biws," + r + ",p=" + sign_b64 + '\0' */
|
||||
response_len = 7 + strlen(r) + 3 + ((alg->digest_size + 2) / 3 * 4) + 1;
|
||||
response = xmpp_alloc(ctx, response_len);
|
||||
if (!response) {
|
||||
goto out_auth;
|
||||
auth_len = 3 + response_len + strlen(first_bare) + strlen(challenge);
|
||||
auth = strophe_alloc(ctx, auth_len);
|
||||
if (!auth) {
|
||||
goto out_response;
|
||||
}
|
||||
|
||||
xmpp_snprintf(response, response_len, "c=biws,%s", r);
|
||||
xmpp_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge,
|
||||
response);
|
||||
l = strophe_snprintf(response, response_len, "c=%s,%s", channel_binding, r);
|
||||
if (l < 0 || (size_t)l >= response_len) {
|
||||
goto out_auth;
|
||||
}
|
||||
l = strophe_snprintf(auth, auth_len, "%s,%s,%s", first_bare, challenge,
|
||||
response);
|
||||
if (l < 0 || (size_t)l >= auth_len) {
|
||||
goto out_auth;
|
||||
}
|
||||
|
||||
SCRAM_ClientKey(alg, (uint8_t *)password, strlen(password), (uint8_t *)sval,
|
||||
sval_len, (uint32_t)ival, key);
|
||||
SCRAM_ClientSignature(alg, key, (uint8_t *)auth, strlen(auth), sign);
|
||||
SCRAM_ClientProof(alg, sign, key, sign);
|
||||
SCRAM_ClientProof(alg, key, sign, sign);
|
||||
|
||||
sign_b64 = xmpp_base64_encode(ctx, sign, alg->digest_size);
|
||||
if (!sign_b64) {
|
||||
goto out_response;
|
||||
goto out_auth;
|
||||
}
|
||||
|
||||
/* Check for buffer overflow */
|
||||
if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) {
|
||||
xmpp_free(ctx, sign_b64);
|
||||
goto out_response;
|
||||
strophe_free(ctx, sign_b64);
|
||||
goto out_auth;
|
||||
}
|
||||
strcat(response, ",p=");
|
||||
strcat(response, sign_b64);
|
||||
xmpp_free(ctx, sign_b64);
|
||||
strophe_free(ctx, sign_b64);
|
||||
|
||||
response_b64 =
|
||||
xmpp_base64_encode(ctx, (unsigned char *)response, strlen(response));
|
||||
if (!response_b64) {
|
||||
goto out_response;
|
||||
goto out_auth;
|
||||
}
|
||||
result = response_b64;
|
||||
|
||||
out_response:
|
||||
xmpp_free(ctx, response);
|
||||
out_auth:
|
||||
xmpp_free(ctx, auth);
|
||||
strophe_free(ctx, auth);
|
||||
out_response:
|
||||
strophe_free(ctx, response);
|
||||
out_sval:
|
||||
xmpp_free(ctx, sval);
|
||||
strophe_free(ctx, sval);
|
||||
out:
|
||||
xmpp_free(ctx, tmp);
|
||||
strophe_free(ctx, tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* sasl.h
|
||||
** strophe XMPP client library -- SASL authentication helpers
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -28,6 +29,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||
const char *password);
|
||||
char *sasl_scram(xmpp_ctx_t *ctx,
|
||||
const struct hash_alg *alg,
|
||||
const char *channel_binding,
|
||||
const char *challenge,
|
||||
const char *first_bare,
|
||||
const char *jid,
|
||||
|
||||
58
src/scram.c
58
src/scram.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* scram.c
|
||||
* strophe XMPP client library
|
||||
*
|
||||
@@ -9,7 +10,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -41,6 +42,15 @@ const struct hash_alg scram_sha1 = {
|
||||
(void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
|
||||
(void (*)(void *, uint8_t *))crypto_SHA1_Final};
|
||||
|
||||
const struct hash_alg scram_sha1_plus = {
|
||||
"SCRAM-SHA-1-PLUS",
|
||||
SASL_MASK_SCRAMSHA1_PLUS,
|
||||
SHA1_DIGEST_SIZE,
|
||||
(void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
|
||||
(void (*)(void *))crypto_SHA1_Init,
|
||||
(void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
|
||||
(void (*)(void *, uint8_t *))crypto_SHA1_Final};
|
||||
|
||||
const struct hash_alg scram_sha256 = {
|
||||
"SCRAM-SHA-256",
|
||||
SASL_MASK_SCRAMSHA256,
|
||||
@@ -50,6 +60,15 @@ const struct hash_alg scram_sha256 = {
|
||||
(void (*)(void *, const uint8_t *, size_t))sha256_process,
|
||||
(void (*)(void *, uint8_t *))sha256_done};
|
||||
|
||||
const struct hash_alg scram_sha256_plus = {
|
||||
"SCRAM-SHA-256-PLUS",
|
||||
SASL_MASK_SCRAMSHA256_PLUS,
|
||||
SHA256_DIGEST_SIZE,
|
||||
(void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
|
||||
(void (*)(void *))sha256_init,
|
||||
(void (*)(void *, const uint8_t *, size_t))sha256_process,
|
||||
(void (*)(void *, uint8_t *))sha256_done};
|
||||
|
||||
const struct hash_alg scram_sha512 = {
|
||||
"SCRAM-SHA-512",
|
||||
SASL_MASK_SCRAMSHA512,
|
||||
@@ -59,6 +78,43 @@ const struct hash_alg scram_sha512 = {
|
||||
(void (*)(void *, const uint8_t *, size_t))sha512_process,
|
||||
(void (*)(void *, uint8_t *))sha512_done};
|
||||
|
||||
const struct hash_alg scram_sha512_plus = {
|
||||
"SCRAM-SHA-512-PLUS",
|
||||
SASL_MASK_SCRAMSHA512_PLUS,
|
||||
SHA512_DIGEST_SIZE,
|
||||
(void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
|
||||
(void (*)(void *))sha512_init,
|
||||
(void (*)(void *, const uint8_t *, size_t))sha512_process,
|
||||
(void (*)(void *, uint8_t *))sha512_done};
|
||||
|
||||
/* The order of this list defines the order in which the SCRAM algorithms are
|
||||
* tried if the server supports them.
|
||||
* Their order is derived from
|
||||
* https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage
|
||||
*/
|
||||
const struct hash_alg *scram_algs[] = {
|
||||
/* *1 */
|
||||
&scram_sha512_plus,
|
||||
/* *1 */
|
||||
&scram_sha256_plus,
|
||||
/* *1 */
|
||||
&scram_sha1_plus,
|
||||
/* *1 */
|
||||
&scram_sha512,
|
||||
/* *1 */
|
||||
&scram_sha256,
|
||||
/* *1 */
|
||||
&scram_sha1,
|
||||
};
|
||||
/* *1 - I want to use clang-format here, but by default it will put multiple
|
||||
* elements per line if there's no comment. Currently clang-format also doesn't
|
||||
* have an option to enforce it to behave like that, besides by putting comments
|
||||
* between each element (or I couldn't find a way to do it). In order to prevent
|
||||
* clang-format from re-formatting the array I added the comments above and
|
||||
* wrote this lengthy description.
|
||||
*/
|
||||
const size_t scram_algs_num = sizeof(scram_algs) / sizeof(scram_algs[0]);
|
||||
|
||||
union common_hash_ctx {
|
||||
SHA1_CTX sha1;
|
||||
sha256_context sha256;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* scram.h
|
||||
* strophe XMPP client library -- SCRAM helper functions
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -33,9 +34,8 @@ struct hash_alg {
|
||||
void (*final)(void *, uint8_t *);
|
||||
};
|
||||
|
||||
extern const struct hash_alg scram_sha1;
|
||||
extern const struct hash_alg scram_sha256;
|
||||
extern const struct hash_alg scram_sha512;
|
||||
extern const struct hash_alg *scram_algs[];
|
||||
extern const size_t scram_algs_num;
|
||||
|
||||
void SCRAM_ClientKey(const struct hash_alg *alg,
|
||||
const uint8_t *password,
|
||||
|
||||
16
src/sha.h
16
src/sha.h
@@ -53,7 +53,7 @@ extern "C" {
|
||||
(y)[0] = (uint8_t)(((x) >> 24) & 255); \
|
||||
(y)[1] = (uint8_t)(((x) >> 16) & 255); \
|
||||
(y)[2] = (uint8_t)(((x) >> 8) & 255); \
|
||||
(y)[3] = (uint8_t)((x)&255); \
|
||||
(y)[3] = (uint8_t)((x) & 255); \
|
||||
} while (0)
|
||||
|
||||
#define LOAD32H(x, y) \
|
||||
@@ -72,7 +72,7 @@ extern "C" {
|
||||
(y)[4] = (uint8_t)(((x) >> 24) & 255); \
|
||||
(y)[5] = (uint8_t)(((x) >> 16) & 255); \
|
||||
(y)[6] = (uint8_t)(((x) >> 8) & 255); \
|
||||
(y)[7] = (uint8_t)((x)&255); \
|
||||
(y)[7] = (uint8_t)((x) & 255); \
|
||||
} while (0)
|
||||
|
||||
#define LOAD64H(x, y) \
|
||||
@@ -96,13 +96,13 @@ extern "C" {
|
||||
#define CONST64(n) n##ULL
|
||||
#endif
|
||||
|
||||
#define RORc(x, y) \
|
||||
(((((uint32_t)(x)&0xFFFFFFFFUL) >> (uint32_t)((y)&31)) | \
|
||||
((uint32_t)(x) << (uint32_t)((32 - ((y)&31)) & 31))) & \
|
||||
#define RORc(x, y) \
|
||||
(((((uint32_t)(x) & 0xFFFFFFFFUL) >> (uint32_t)((y) & 31)) | \
|
||||
((uint32_t)(x) << (uint32_t)((32 - ((y) & 31)) & 31))) & \
|
||||
0xFFFFFFFFUL)
|
||||
#define ROR64c(x, y) \
|
||||
(((((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y)&CONST64(63))) | \
|
||||
((x) << (((uint64_t)64 - ((y)&63)) & 63))) & \
|
||||
#define ROR64c(x, y) \
|
||||
(((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y) & CONST64(63))) | \
|
||||
((x) << (((uint64_t)64 - ((y) & 63)) & 63))) & \
|
||||
CONST64(0xFFFFFFFFFFFFFFFF))
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -206,9 +206,7 @@ void crypto_SHA1_Init(SHA1_CTX *context)
|
||||
}
|
||||
|
||||
/* Run your data through this. */
|
||||
void crypto_SHA1_Update(SHA1_CTX *context,
|
||||
const uint8_t *data,
|
||||
const size_t len)
|
||||
void crypto_SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
|
||||
@@ -24,9 +24,7 @@ typedef struct {
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
void crypto_SHA1_Init(SHA1_CTX *context);
|
||||
void crypto_SHA1_Update(SHA1_CTX *context,
|
||||
const uint8_t *data,
|
||||
const size_t len);
|
||||
void crypto_SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len);
|
||||
void crypto_SHA1_Final(SHA1_CTX *context, uint8_t *digest);
|
||||
void crypto_SHA1(const uint8_t *data, size_t len, uint8_t *digest);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define Maj(x, y, z) (((x | y) & z) | (x & y))
|
||||
#define S(x, n) RORc((x), (n))
|
||||
#define R(x, n) (((x)&0xFFFFFFFFUL) >> (n))
|
||||
#define R(x, n) (((x) & 0xFFFFFFFFUL) >> (n))
|
||||
#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
|
||||
#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
|
||||
#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
|
||||
|
||||
@@ -52,7 +52,7 @@ static const uint64_t K[80] = {
|
||||
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define Maj(x, y, z) (((x | y) & z) | (x & y))
|
||||
#define S(x, n) ROR64c(x, n)
|
||||
#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)n))
|
||||
#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)n))
|
||||
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
|
||||
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
|
||||
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
/* JAM: we don't need this - #include "config.h" */
|
||||
|
||||
/* JAM: changed declarations to xmpp_snprintf and xmpp_vsnprintf to
|
||||
/* JAM: changed declarations to strophe_snprintf and strophe_vsnprintf to
|
||||
avoid namespace collision. */
|
||||
|
||||
#include "snprintf.h"
|
||||
@@ -706,7 +706,7 @@ static int dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
|
||||
int strophe_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
|
||||
{
|
||||
if (str != NULL && count > 0)
|
||||
str[0] = 0;
|
||||
@@ -716,13 +716,13 @@ int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
/* VARARGS3 */
|
||||
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
|
||||
int strophe_snprintf(char *str, size_t count, const char *fmt, ...)
|
||||
{
|
||||
VA_LOCAL_DECL;
|
||||
int total;
|
||||
|
||||
VA_START(fmt);
|
||||
total = xmpp_vsnprintf(str, count, fmt, ap);
|
||||
total = strophe_vsnprintf(str, count, fmt, ap);
|
||||
VA_END;
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SNPRINTF
|
||||
#define xmpp_snprintf snprintf
|
||||
#define strophe_snprintf snprintf
|
||||
#else
|
||||
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...);
|
||||
int strophe_snprintf(char *str, size_t count, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSNPRINTF
|
||||
#define xmpp_vsnprintf vsnprintf
|
||||
#define strophe_vsnprintf vsnprintf
|
||||
#else
|
||||
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
|
||||
int strophe_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
|
||||
#endif
|
||||
|
||||
#endif /* __LIBSTROPHE_SNPRINTF_H__ */
|
||||
|
||||
306
src/sock.c
306
src/sock.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* sock.c
|
||||
** strophe XMPP client library -- socket abstraction implementation
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <iphlpapi.h>
|
||||
#include <mstcpip.h> /* tcp_keepalive */
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -33,8 +35,31 @@
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "sock.h"
|
||||
#include "snprintf.h"
|
||||
#include "common.h"
|
||||
#include "resolver.h"
|
||||
|
||||
const struct conn_interface sock_intf = {
|
||||
sock_read,
|
||||
sock_write,
|
||||
/* no flush */
|
||||
conn_int_nop,
|
||||
/* no pending */
|
||||
conn_int_nop,
|
||||
sock_error,
|
||||
sock_is_recoverable,
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct _xmpp_sock_t {
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
struct addrinfo *ainfo_list;
|
||||
struct addrinfo *ainfo_cur;
|
||||
resolver_srv_rr_t *srv_rr_list;
|
||||
resolver_srv_rr_t *srv_rr_cur;
|
||||
const char *host;
|
||||
unsigned short port;
|
||||
};
|
||||
|
||||
void sock_initialize(void)
|
||||
{
|
||||
@@ -51,8 +76,9 @@ void sock_shutdown(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_error(void)
|
||||
int sock_error(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(intf);
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
@@ -69,52 +95,181 @@ static int _in_progress(int error)
|
||||
#endif
|
||||
}
|
||||
|
||||
sock_t sock_connect(const char *const host, const unsigned short port)
|
||||
static void sock_getaddrinfo(xmpp_sock_t *xsock)
|
||||
{
|
||||
sock_t sock;
|
||||
char service[6];
|
||||
struct addrinfo *res, *ainfo, hints;
|
||||
int err;
|
||||
struct addrinfo hints;
|
||||
int rc;
|
||||
|
||||
xmpp_snprintf(service, 6, "%u", port);
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
err = getaddrinfo(host, service, &hints, &res);
|
||||
if (err != 0)
|
||||
return -1;
|
||||
|
||||
for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
|
||||
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
|
||||
if (sock < 0)
|
||||
continue;
|
||||
|
||||
err = sock_set_nonblocking(sock);
|
||||
if (err == 0) {
|
||||
err = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
|
||||
if (err == 0 || _in_progress(sock_error()))
|
||||
break;
|
||||
}
|
||||
sock_close(sock);
|
||||
if (xsock->ainfo_list) {
|
||||
freeaddrinfo(xsock->ainfo_list);
|
||||
xsock->ainfo_list = NULL;
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
sock = ainfo == NULL ? -1 : sock;
|
||||
|
||||
if (xsock->srv_rr_cur) {
|
||||
/* Cache host and port for debug logs. */
|
||||
xsock->host = xsock->srv_rr_cur->target;
|
||||
xsock->port = xsock->srv_rr_cur->port;
|
||||
|
||||
strophe_snprintf(service, 6, "%u", xsock->srv_rr_cur->port);
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
|
||||
&xsock->ainfo_list);
|
||||
if (rc != 0) {
|
||||
strophe_debug(xsock->ctx, "sock",
|
||||
"getaddrinfo() failed with %s (%d)", gai_strerror(rc),
|
||||
rc);
|
||||
xsock->ainfo_list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
xsock->ainfo_cur = xsock->ainfo_list;
|
||||
}
|
||||
|
||||
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
|
||||
const char *domain,
|
||||
const char *host,
|
||||
unsigned short port)
|
||||
{
|
||||
xmpp_ctx_t *ctx = conn->ctx;
|
||||
xmpp_sock_t *xsock;
|
||||
int found = XMPP_DOMAIN_NOT_FOUND;
|
||||
|
||||
xsock = strophe_alloc(ctx, sizeof(*xsock));
|
||||
if (!xsock) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xsock->ctx = ctx;
|
||||
xsock->conn = conn;
|
||||
xsock->host = NULL;
|
||||
xsock->port = 0;
|
||||
|
||||
if (!host) {
|
||||
found = resolver_srv_lookup(ctx, "xmpp-client", "tcp", domain,
|
||||
&xsock->srv_rr_list);
|
||||
if (XMPP_DOMAIN_NOT_FOUND == found)
|
||||
strophe_debug(ctx, "sock",
|
||||
"SRV lookup failed, connecting via domain.");
|
||||
}
|
||||
if (XMPP_DOMAIN_NOT_FOUND == found) {
|
||||
/* Resolution failed or the host is provided explicitly. */
|
||||
xsock->srv_rr_list =
|
||||
resolver_srv_rr_new(ctx, host ? host : domain, port, 0, 0);
|
||||
}
|
||||
xsock->srv_rr_cur = xsock->srv_rr_list;
|
||||
|
||||
xsock->ainfo_list = NULL;
|
||||
sock_getaddrinfo(xsock);
|
||||
if (xsock->srv_rr_cur)
|
||||
xsock->srv_rr_cur = xsock->srv_rr_cur->next;
|
||||
|
||||
return xsock;
|
||||
}
|
||||
|
||||
void sock_free(xmpp_sock_t *xsock)
|
||||
{
|
||||
if (!xsock)
|
||||
return;
|
||||
|
||||
if (xsock->ainfo_list)
|
||||
freeaddrinfo(xsock->ainfo_list);
|
||||
if (xsock->srv_rr_list)
|
||||
resolver_srv_free(xsock->ctx, xsock->srv_rr_list);
|
||||
strophe_free(xsock->ctx, xsock);
|
||||
}
|
||||
|
||||
static const char *_sockaddr2str(struct sockaddr *sa, char *buf, size_t buflen)
|
||||
{
|
||||
buf[0] = '\0';
|
||||
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, buflen);
|
||||
break;
|
||||
case AF_INET6:
|
||||
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf,
|
||||
buflen);
|
||||
break;
|
||||
default:
|
||||
strophe_snprintf(buf, buflen, "<Unknown>");
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
sock_t sock_connect(xmpp_sock_t *xsock)
|
||||
{
|
||||
struct addrinfo *ainfo;
|
||||
sock_t sock;
|
||||
int rc;
|
||||
char buf[64];
|
||||
|
||||
do {
|
||||
if (!xsock->ainfo_cur) {
|
||||
sock_getaddrinfo(xsock);
|
||||
if (xsock->srv_rr_cur)
|
||||
xsock->srv_rr_cur = xsock->srv_rr_cur->next;
|
||||
}
|
||||
if (!xsock->ainfo_cur) {
|
||||
/* We tried all available addresses. */
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
ainfo = xsock->ainfo_cur;
|
||||
strophe_debug(xsock->ctx, "sock", "Connecting to %s:%u via %s",
|
||||
xsock->host, xsock->port,
|
||||
_sockaddr2str(ainfo->ai_addr, buf, sizeof(buf)));
|
||||
|
||||
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
|
||||
if (sock != INVALID_SOCKET) {
|
||||
rc = 0;
|
||||
if (xsock->conn->sockopt_cb) {
|
||||
/* Don't allow user to overwrite sockfd value. */
|
||||
sock_t sock_copy = sock;
|
||||
rc = xsock->conn->sockopt_cb(xsock->conn, &sock_copy);
|
||||
if (rc != 0) {
|
||||
strophe_debug(xsock->ctx, "sock",
|
||||
"User's setsockopt callback"
|
||||
" failed with %d (errno=%d)",
|
||||
rc, errno);
|
||||
}
|
||||
}
|
||||
if (rc == 0)
|
||||
rc = sock_set_nonblocking(sock);
|
||||
if (rc == 0)
|
||||
rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
|
||||
/* Assume only connect() can cause "in progress" error. */
|
||||
if (rc != 0 && !_in_progress(sock_error(NULL))) {
|
||||
sock_close(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
strophe_debug(xsock->ctx, "sock", "sock_connect() result %d", sock);
|
||||
|
||||
xsock->ainfo_cur = xsock->ainfo_cur->ai_next;
|
||||
} while (sock == INVALID_SOCKET);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int sock_set_keepalive(const sock_t sock, int timeout, int interval)
|
||||
int sock_set_keepalive(sock_t sock,
|
||||
int timeout,
|
||||
int interval,
|
||||
int count,
|
||||
unsigned int user_timeout)
|
||||
{
|
||||
int ret;
|
||||
int optval = (timeout && interval) ? 1 : 0;
|
||||
|
||||
/* This function doesn't change maximum number of keepalive probes */
|
||||
UNUSED(count);
|
||||
UNUSED(user_timeout);
|
||||
|
||||
#ifdef _WIN32
|
||||
struct tcp_keepalive ka;
|
||||
@@ -149,12 +304,58 @@ int sock_set_keepalive(const sock_t sock, int timeout, int interval)
|
||||
return ret;
|
||||
#endif /* TCP_KEEPINTVL */
|
||||
}
|
||||
|
||||
if (count) {
|
||||
#ifdef TCP_KEEPCNT
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif /* TCP_KEEPCNT */
|
||||
}
|
||||
|
||||
if (user_timeout) {
|
||||
#ifdef TCP_USER_TIMEOUT
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
|
||||
sizeof(user_timeout));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#elif defined(TCP_RXT_CONNDROPTIME)
|
||||
int rxt = user_timeout / 1000;
|
||||
ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
|
||||
sizeof(rxt));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif /* TCP_USER_TIMEOUT */
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sock_close(const sock_t sock)
|
||||
/** Example sockopt callback function
|
||||
* An example function that can be used to set reasonable default keepalive
|
||||
* options on sockets when registered for a connection with
|
||||
* xmpp_conn_set_sockopt_callback()
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
* @param socket pointer to a socket descriptor
|
||||
*
|
||||
* @see xmpp_sockopt_callback for details on the `socket` parameter
|
||||
* @ingroup Connections
|
||||
*/
|
||||
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *socket)
|
||||
{
|
||||
sock_t sock = *((sock_t *)socket);
|
||||
|
||||
return sock_set_keepalive(
|
||||
sock, conn->ka_timeout, conn->ka_interval, conn->ka_count,
|
||||
conn->ka_count
|
||||
? (conn->ka_timeout + conn->ka_interval * conn->ka_count) * 1000
|
||||
: 0);
|
||||
}
|
||||
|
||||
int sock_close(sock_t sock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return closesocket(sock);
|
||||
@@ -180,28 +381,29 @@ static int _sock_set_blocking_mode(sock_t sock, int blocking)
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_set_blocking(const sock_t sock)
|
||||
int sock_set_blocking(sock_t sock)
|
||||
{
|
||||
return _sock_set_blocking_mode(sock, 1);
|
||||
}
|
||||
|
||||
int sock_set_nonblocking(const sock_t sock)
|
||||
int sock_set_nonblocking(sock_t sock)
|
||||
{
|
||||
return _sock_set_blocking_mode(sock, 0);
|
||||
}
|
||||
|
||||
int sock_read(const sock_t sock, void *const buff, const size_t len)
|
||||
int sock_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
return recv(sock, buff, len, 0);
|
||||
return recv(intf->conn->sock, buff, len, 0);
|
||||
}
|
||||
|
||||
int sock_write(const sock_t sock, const void *const buff, const size_t len)
|
||||
int sock_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
return send(sock, buff, len, 0);
|
||||
return send(intf->conn->sock, buff, len, 0);
|
||||
}
|
||||
|
||||
int sock_is_recoverable(const int error)
|
||||
int sock_is_recoverable(struct conn_interface *intf, int error)
|
||||
{
|
||||
UNUSED(intf);
|
||||
#ifdef _WIN32
|
||||
return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
|
||||
error == WSAEINPROGRESS);
|
||||
@@ -210,7 +412,7 @@ int sock_is_recoverable(const int error)
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_connect_error(const sock_t sock)
|
||||
int sock_connect_error(sock_t sock)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr *sa = (struct sockaddr *)&ss;
|
||||
@@ -230,15 +432,15 @@ int sock_connect_error(const sock_t sock)
|
||||
/* it's possible that the error wasn't ENOTCONN, so if it wasn't,
|
||||
* return that */
|
||||
#ifdef _WIN32
|
||||
if (sock_error() != WSAENOTCONN)
|
||||
return sock_error();
|
||||
if (sock_error(NULL) != WSAENOTCONN)
|
||||
return sock_error(NULL);
|
||||
#else
|
||||
if (sock_error() != ENOTCONN)
|
||||
return sock_error();
|
||||
if (sock_error(NULL) != ENOTCONN)
|
||||
return sock_error(NULL);
|
||||
#endif
|
||||
|
||||
/* load the correct error into errno through error slippage */
|
||||
recv(sock, &temp, 1, 0);
|
||||
|
||||
return sock_error();
|
||||
return sock_error(NULL);
|
||||
}
|
||||
|
||||
40
src/sock.h
40
src/sock.h
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* sock.h
|
||||
** strophe XMPP client library -- socket abstraction header
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -20,26 +21,43 @@
|
||||
|
||||
#ifndef _WIN32
|
||||
typedef int sock_t;
|
||||
#define INVALID_SOCKET (-1)
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <mstcpip.h> /* tcp_keepalive */
|
||||
typedef SOCKET sock_t;
|
||||
#endif
|
||||
|
||||
extern const struct conn_interface sock_intf;
|
||||
|
||||
typedef struct _xmpp_sock_t xmpp_sock_t;
|
||||
|
||||
void sock_initialize(void);
|
||||
void sock_shutdown(void);
|
||||
|
||||
int sock_error(void);
|
||||
int sock_error(struct conn_interface *intf);
|
||||
|
||||
sock_t sock_connect(const char *const host, const unsigned short port);
|
||||
int sock_close(const sock_t sock);
|
||||
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
|
||||
const char *domain,
|
||||
const char *host,
|
||||
unsigned short port);
|
||||
void sock_free(xmpp_sock_t *xsock);
|
||||
sock_t sock_connect(xmpp_sock_t *xsock);
|
||||
int sock_close(sock_t sock);
|
||||
|
||||
int sock_set_blocking(const sock_t sock);
|
||||
int sock_set_nonblocking(const sock_t sock);
|
||||
int sock_read(const sock_t sock, void *const buff, const size_t len);
|
||||
int sock_write(const sock_t sock, const void *const buff, const size_t len);
|
||||
int sock_is_recoverable(const int error);
|
||||
int sock_set_blocking(sock_t sock);
|
||||
int sock_set_nonblocking(sock_t sock);
|
||||
int sock_read(struct conn_interface *intf, void *buff, size_t len);
|
||||
int sock_write(struct conn_interface *intf, const void *buff, size_t len);
|
||||
int sock_is_recoverable(struct conn_interface *intf, int error);
|
||||
/* checks for an error after connect, return 0 if connect successful */
|
||||
int sock_connect_error(const sock_t sock);
|
||||
int sock_set_keepalive(const sock_t sock, int timeout, int interval);
|
||||
int sock_connect_error(sock_t sock);
|
||||
int sock_set_keepalive(sock_t sock,
|
||||
int timeout,
|
||||
int interval,
|
||||
int count,
|
||||
unsigned int user_timeout);
|
||||
|
||||
#endif /* __LIBSTROPHE_SOCK_H__ */
|
||||
|
||||
377
src/stanza.c
377
src/stanza.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* stanza.c
|
||||
** strophe XMPP client library -- XMPP stanza object and utilities
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -39,17 +40,12 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
stanza = xmpp_alloc(ctx, sizeof(xmpp_stanza_t));
|
||||
stanza = strophe_alloc(ctx, sizeof(xmpp_stanza_t));
|
||||
if (stanza != NULL) {
|
||||
memset(stanza, 0, sizeof(xmpp_stanza_t));
|
||||
stanza->ref = 1;
|
||||
stanza->ctx = ctx;
|
||||
stanza->type = XMPP_STANZA_UNKNOWN;
|
||||
stanza->prev = NULL;
|
||||
stanza->next = NULL;
|
||||
stanza->children = NULL;
|
||||
stanza->parent = NULL;
|
||||
stanza->data = NULL;
|
||||
stanza->attributes = NULL;
|
||||
}
|
||||
|
||||
return stanza;
|
||||
@@ -64,7 +60,7 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza)
|
||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *stanza)
|
||||
{
|
||||
stanza->ref++;
|
||||
|
||||
@@ -74,8 +70,7 @@ xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza)
|
||||
/*
|
||||
* Copy the attributes of stanza src into stanza dst. Return -1 on error.
|
||||
*/
|
||||
static int _stanza_copy_attributes(xmpp_stanza_t *dst,
|
||||
const xmpp_stanza_t *const src)
|
||||
static int _stanza_copy_attributes(xmpp_stanza_t *dst, const xmpp_stanza_t *src)
|
||||
{
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
@@ -114,7 +109,7 @@ static int _stanza_copy_attributes(xmpp_stanza_t *dst,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *const stanza)
|
||||
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *stanza)
|
||||
{
|
||||
xmpp_stanza_t *copy, *child, *copychild, *tail;
|
||||
|
||||
@@ -125,7 +120,7 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *const stanza)
|
||||
copy->type = stanza->type;
|
||||
|
||||
if (stanza->data) {
|
||||
copy->data = xmpp_strdup(stanza->ctx, stanza->data);
|
||||
copy->data = strophe_strdup(stanza->ctx, stanza->data);
|
||||
if (!copy->data)
|
||||
goto copy_error;
|
||||
}
|
||||
@@ -169,7 +164,7 @@ copy_error:
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_release(xmpp_stanza_t *const stanza)
|
||||
int xmpp_stanza_release(xmpp_stanza_t *stanza)
|
||||
{
|
||||
int released = 0;
|
||||
xmpp_stanza_t *child, *tchild;
|
||||
@@ -183,14 +178,15 @@ int xmpp_stanza_release(xmpp_stanza_t *const stanza)
|
||||
while (child) {
|
||||
tchild = child;
|
||||
child = child->next;
|
||||
tchild->next = NULL;
|
||||
xmpp_stanza_release(tchild);
|
||||
}
|
||||
|
||||
if (stanza->attributes)
|
||||
hash_release(stanza->attributes);
|
||||
if (stanza->data)
|
||||
xmpp_free(stanza->ctx, stanza->data);
|
||||
xmpp_free(stanza->ctx, stanza);
|
||||
strophe_free(stanza->ctx, stanza->data);
|
||||
strophe_free(stanza->ctx, stanza);
|
||||
released = 1;
|
||||
}
|
||||
|
||||
@@ -205,7 +201,7 @@ int xmpp_stanza_release(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza)
|
||||
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *stanza)
|
||||
{
|
||||
return stanza->ctx;
|
||||
}
|
||||
@@ -218,7 +214,7 @@ xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_is_text(xmpp_stanza_t *const stanza)
|
||||
int xmpp_stanza_is_text(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return (stanza && stanza->type == XMPP_STANZA_TEXT);
|
||||
}
|
||||
@@ -231,7 +227,7 @@ int xmpp_stanza_is_text(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_is_tag(xmpp_stanza_t *const stanza)
|
||||
int xmpp_stanza_is_tag(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return (stanza && stanza->type == XMPP_STANZA_TAG);
|
||||
}
|
||||
@@ -242,7 +238,7 @@ int xmpp_stanza_is_tag(xmpp_stanza_t *const stanza)
|
||||
* On failure, returns NULL.
|
||||
*/
|
||||
|
||||
static char *_escape_xml(xmpp_ctx_t *const ctx, char *text)
|
||||
static char *_escape_xml(xmpp_ctx_t *ctx, char *text)
|
||||
{
|
||||
size_t len = 0;
|
||||
char *src;
|
||||
@@ -264,7 +260,7 @@ static char *_escape_xml(xmpp_ctx_t *const ctx, char *text)
|
||||
len++;
|
||||
}
|
||||
}
|
||||
if ((buf = xmpp_alloc(ctx, (len + 1) * sizeof(char))) == NULL)
|
||||
if ((buf = strophe_alloc(ctx, (len + 1) * sizeof(char))) == NULL)
|
||||
return NULL; /* Error */
|
||||
dst = buf;
|
||||
for (src = text; *src != '\0'; src++) {
|
||||
@@ -295,11 +291,8 @@ static char *_escape_xml(xmpp_ctx_t *const ctx, char *text)
|
||||
}
|
||||
|
||||
/* small helper function */
|
||||
static void _render_update(int *written,
|
||||
const int length,
|
||||
const int lastwrite,
|
||||
size_t *left,
|
||||
char **ptr)
|
||||
static void _render_update(
|
||||
int *written, int length, int lastwrite, size_t *left, char **ptr)
|
||||
{
|
||||
*written += lastwrite;
|
||||
|
||||
@@ -317,9 +310,8 @@ static void _render_update(int *written,
|
||||
* return values < 0 indicate some error occurred,
|
||||
* and return values > buflen indicate buffer was not large enough
|
||||
*/
|
||||
static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
char *const buf,
|
||||
size_t const buflen)
|
||||
static int
|
||||
_render_stanza_recursive(xmpp_stanza_t *stanza, char *buf, size_t buflen)
|
||||
{
|
||||
char *ptr = buf;
|
||||
size_t left = buflen;
|
||||
@@ -341,8 +333,8 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
tmp = _escape_xml(stanza->ctx, stanza->data);
|
||||
if (tmp == NULL)
|
||||
return XMPP_EMEM;
|
||||
ret = xmpp_snprintf(ptr, left, "%s", tmp);
|
||||
xmpp_free(stanza->ctx, tmp);
|
||||
ret = strophe_snprintf(ptr, left, "%s", tmp);
|
||||
strophe_free(stanza->ctx, tmp);
|
||||
if (ret < 0)
|
||||
return XMPP_EMEM;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
@@ -351,7 +343,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
return XMPP_EINVOP;
|
||||
|
||||
/* write beginning of tag and attributes */
|
||||
ret = xmpp_snprintf(ptr, left, "<%s", stanza->data);
|
||||
ret = strophe_snprintf(ptr, left, "<%s", stanza->data);
|
||||
if (ret < 0)
|
||||
return XMPP_EMEM;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
@@ -379,8 +371,8 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
hash_iter_release(iter);
|
||||
return XMPP_EMEM;
|
||||
}
|
||||
ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
|
||||
xmpp_free(stanza->ctx, tmp);
|
||||
ret = strophe_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
|
||||
strophe_free(stanza->ctx, tmp);
|
||||
if (ret < 0) {
|
||||
hash_iter_release(iter);
|
||||
return XMPP_EMEM;
|
||||
@@ -392,7 +384,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
|
||||
if (!stanza->children) {
|
||||
/* write end if singleton tag */
|
||||
ret = xmpp_snprintf(ptr, left, "/>");
|
||||
ret = strophe_snprintf(ptr, left, "/>");
|
||||
if (ret < 0)
|
||||
return XMPP_EMEM;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
@@ -400,7 +392,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
/* this stanza has child stanzas */
|
||||
|
||||
/* write end of start tag */
|
||||
ret = xmpp_snprintf(ptr, left, ">");
|
||||
ret = strophe_snprintf(ptr, left, ">");
|
||||
if (ret < 0)
|
||||
return XMPP_EMEM;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
@@ -418,7 +410,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
}
|
||||
|
||||
/* write end tag */
|
||||
ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data);
|
||||
ret = strophe_snprintf(ptr, left, "</%s>", stanza->data);
|
||||
if (ret < 0)
|
||||
return XMPP_EMEM;
|
||||
|
||||
@@ -444,9 +436,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
char **const buf,
|
||||
size_t *const buflen)
|
||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza, char **buf, size_t *buflen)
|
||||
{
|
||||
char *buffer, *tmp;
|
||||
size_t length;
|
||||
@@ -454,7 +444,7 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
|
||||
/* allocate a default sized buffer and attempt to render */
|
||||
length = 1024;
|
||||
buffer = xmpp_alloc(stanza->ctx, length);
|
||||
buffer = strophe_alloc(stanza->ctx, length);
|
||||
if (!buffer) {
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
@@ -463,16 +453,16 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
|
||||
ret = _render_stanza_recursive(stanza, buffer, length);
|
||||
if (ret < 0) {
|
||||
xmpp_free(stanza->ctx, buffer);
|
||||
strophe_free(stanza->ctx, buffer);
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((size_t)ret > length - 1) {
|
||||
tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1);
|
||||
tmp = strophe_realloc(stanza->ctx, buffer, ret + 1);
|
||||
if (!tmp) {
|
||||
xmpp_free(stanza->ctx, buffer);
|
||||
strophe_free(stanza->ctx, buffer);
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
return XMPP_EMEM;
|
||||
@@ -482,7 +472,7 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
|
||||
ret = _render_stanza_recursive(stanza, buffer, length);
|
||||
if ((size_t)ret > length - 1) {
|
||||
xmpp_free(stanza->ctx, buffer);
|
||||
strophe_free(stanza->ctx, buffer);
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
return XMPP_EMEM;
|
||||
@@ -507,16 +497,16 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name)
|
||||
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *name)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TEXT)
|
||||
return XMPP_EINVOP;
|
||||
|
||||
if (stanza->data)
|
||||
xmpp_free(stanza->ctx, stanza->data);
|
||||
strophe_free(stanza->ctx, stanza->data);
|
||||
|
||||
stanza->type = XMPP_STANZA_TAG;
|
||||
stanza->data = xmpp_strdup(stanza->ctx, name);
|
||||
stanza->data = strophe_strdup(stanza->ctx, name);
|
||||
|
||||
return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
|
||||
}
|
||||
@@ -531,7 +521,7 @@ int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza)
|
||||
const char *xmpp_stanza_get_name(xmpp_stanza_t *stanza)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TEXT)
|
||||
return NULL;
|
||||
@@ -546,7 +536,7 @@ const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza)
|
||||
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *stanza)
|
||||
{
|
||||
if (stanza->attributes == NULL) {
|
||||
return 0;
|
||||
@@ -569,7 +559,7 @@ int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
|
||||
int xmpp_stanza_get_attributes(xmpp_stanza_t *stanza,
|
||||
const char **attr,
|
||||
int attrlen)
|
||||
{
|
||||
@@ -611,9 +601,9 @@ int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
|
||||
const char *const key,
|
||||
const char *const value)
|
||||
int xmpp_stanza_set_attribute(xmpp_stanza_t *stanza,
|
||||
const char *key,
|
||||
const char *value)
|
||||
{
|
||||
char *val;
|
||||
int rc;
|
||||
@@ -622,19 +612,19 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
|
||||
return XMPP_EINVOP;
|
||||
|
||||
if (!stanza->attributes) {
|
||||
stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
|
||||
stanza->attributes = hash_new(stanza->ctx, 8, strophe_free);
|
||||
if (!stanza->attributes)
|
||||
return XMPP_EMEM;
|
||||
}
|
||||
|
||||
val = xmpp_strdup(stanza->ctx, value);
|
||||
val = strophe_strdup(stanza->ctx, value);
|
||||
if (!val) {
|
||||
return XMPP_EMEM;
|
||||
}
|
||||
|
||||
rc = hash_add(stanza->attributes, key, val);
|
||||
if (rc < 0) {
|
||||
xmpp_free(stanza->ctx, val);
|
||||
strophe_free(stanza->ctx, val);
|
||||
return XMPP_EMEM;
|
||||
}
|
||||
|
||||
@@ -652,7 +642,7 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_ns(xmpp_stanza_t *const stanza, const char *const ns)
|
||||
int xmpp_stanza_set_ns(xmpp_stanza_t *stanza, const char *ns)
|
||||
{
|
||||
return xmpp_stanza_set_attribute(stanza, "xmlns", ns);
|
||||
}
|
||||
@@ -727,7 +717,7 @@ int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text)
|
||||
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *text)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TAG)
|
||||
return XMPP_EINVOP;
|
||||
@@ -735,8 +725,8 @@ int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text)
|
||||
stanza->type = XMPP_STANZA_TEXT;
|
||||
|
||||
if (stanza->data)
|
||||
xmpp_free(stanza->ctx, stanza->data);
|
||||
stanza->data = xmpp_strdup(stanza->ctx, text);
|
||||
strophe_free(stanza->ctx, stanza->data);
|
||||
stanza->data = strophe_strdup(stanza->ctx, text);
|
||||
|
||||
return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
|
||||
}
|
||||
@@ -756,8 +746,8 @@ int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text)
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||
const char *const text,
|
||||
const size_t size)
|
||||
const char *text,
|
||||
size_t size)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TAG)
|
||||
return XMPP_EINVOP;
|
||||
@@ -765,8 +755,8 @@ int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||
stanza->type = XMPP_STANZA_TEXT;
|
||||
|
||||
if (stanza->data)
|
||||
xmpp_free(stanza->ctx, stanza->data);
|
||||
stanza->data = xmpp_alloc(stanza->ctx, size + 1);
|
||||
strophe_free(stanza->ctx, stanza->data);
|
||||
stanza->data = strophe_alloc(stanza->ctx, size + 1);
|
||||
if (!stanza->data)
|
||||
return XMPP_EMEM;
|
||||
|
||||
@@ -786,7 +776,7 @@ int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_id(xmpp_stanza_t *const stanza)
|
||||
const char *xmpp_stanza_get_id(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return xmpp_stanza_get_attribute(stanza, "id");
|
||||
}
|
||||
@@ -801,7 +791,7 @@ const char *xmpp_stanza_get_id(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_ns(xmpp_stanza_t *const stanza)
|
||||
const char *xmpp_stanza_get_ns(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return xmpp_stanza_get_attribute(stanza, "xmlns");
|
||||
}
|
||||
@@ -816,7 +806,7 @@ const char *xmpp_stanza_get_ns(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_type(xmpp_stanza_t *const stanza)
|
||||
const char *xmpp_stanza_get_type(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return xmpp_stanza_get_attribute(stanza, "type");
|
||||
}
|
||||
@@ -831,7 +821,7 @@ const char *xmpp_stanza_get_type(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_to(xmpp_stanza_t *const stanza)
|
||||
const char *xmpp_stanza_get_to(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return xmpp_stanza_get_attribute(stanza, "to");
|
||||
}
|
||||
@@ -846,11 +836,87 @@ const char *xmpp_stanza_get_to(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_from(xmpp_stanza_t *const stanza)
|
||||
const char *xmpp_stanza_get_from(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return xmpp_stanza_get_attribute(stanza, "from");
|
||||
}
|
||||
|
||||
/** Get the first child of stanza following a path-like list of names.
|
||||
* This function searches the children and their children that match
|
||||
* the given path.
|
||||
*
|
||||
* * "name" - Search 'name'
|
||||
*
|
||||
* * "name[@ns='foo']" - Search 'name' which is in the namespace 'foo'
|
||||
*
|
||||
* The Syntax to pass namespaces is inspired by the XPATH way of passing
|
||||
* attributes.
|
||||
*
|
||||
* The namespace syntax only supports single quotes `'`.
|
||||
*
|
||||
* The \ref XMPP_STANZA_NAME_IN_NS macro is provided as a helper for names
|
||||
* in namespaces.
|
||||
*
|
||||
* @param stanza a Strophe stanza object
|
||||
* @param ... a var-args list that must be terminated by a NULL entry
|
||||
*
|
||||
* @return the matching child stanza object or NULL if no match was found
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_path(xmpp_stanza_t *stanza, ...)
|
||||
{
|
||||
xmpp_stanza_t *child = NULL;
|
||||
char *p, *tok, *attr, *saveattr, *ns = NULL;
|
||||
const char *xmlns;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, stanza);
|
||||
|
||||
while ((p = va_arg(ap, char *)) != NULL) {
|
||||
tok = strophe_strdup(stanza->ctx, p);
|
||||
if (!tok) {
|
||||
child = NULL;
|
||||
break;
|
||||
}
|
||||
saveattr = ns = NULL;
|
||||
attr = strophe_strtok_r(tok, "[", &saveattr);
|
||||
if (attr) {
|
||||
attr = strophe_strtok_r(NULL, "]", &saveattr);
|
||||
if (attr) {
|
||||
if (!strncmp(attr, "@ns='", 5)) {
|
||||
ns = attr + 5;
|
||||
strophe_strtok_r(ns, "'", &saveattr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!child) {
|
||||
if (strcmp(xmpp_stanza_get_name(stanza), tok))
|
||||
goto error_out;
|
||||
|
||||
if (ns) {
|
||||
xmlns = xmpp_stanza_get_ns(stanza);
|
||||
if (!xmlns || strcmp(xmlns, ns))
|
||||
goto error_out;
|
||||
}
|
||||
child = stanza;
|
||||
} else {
|
||||
if (!ns)
|
||||
child = xmpp_stanza_get_child_by_name(child, tok);
|
||||
else
|
||||
child = xmpp_stanza_get_child_by_name_and_ns(child, tok, ns);
|
||||
}
|
||||
error_out:
|
||||
strophe_free(stanza->ctx, tok);
|
||||
if (!child)
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return p == NULL ? child : NULL;
|
||||
}
|
||||
|
||||
/** Get the first child of stanza with name.
|
||||
* This function searches all the immediate children of stanza for a child
|
||||
* stanza that matches the name. The first matching child is returned.
|
||||
@@ -862,8 +928,8 @@ const char *xmpp_stanza_get_from(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *const stanza,
|
||||
const char *const name)
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *stanza,
|
||||
const char *name)
|
||||
{
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
@@ -888,8 +954,8 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *const stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza,
|
||||
const char *const ns)
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *stanza,
|
||||
const char *ns)
|
||||
{
|
||||
xmpp_stanza_t *child;
|
||||
const char *child_ns;
|
||||
@@ -916,9 +982,9 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
|
||||
const char *const name,
|
||||
const char *const ns)
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *stanza,
|
||||
const char *name,
|
||||
const char *ns)
|
||||
{
|
||||
xmpp_stanza_t *child;
|
||||
const char *child_ns;
|
||||
@@ -947,7 +1013,7 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *const stanza)
|
||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return stanza->children;
|
||||
}
|
||||
@@ -960,7 +1026,7 @@ xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza)
|
||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *stanza)
|
||||
{
|
||||
return stanza->next;
|
||||
}
|
||||
@@ -976,7 +1042,7 @@ xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
|
||||
char *xmpp_stanza_get_text(xmpp_stanza_t *stanza)
|
||||
{
|
||||
size_t len, clen;
|
||||
xmpp_stanza_t *child;
|
||||
@@ -984,7 +1050,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
|
||||
|
||||
if (stanza->type == XMPP_STANZA_TEXT) {
|
||||
if (stanza->data)
|
||||
return xmpp_strdup(stanza->ctx, stanza->data);
|
||||
return strophe_strdup(stanza->ctx, stanza->data);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@@ -997,7 +1063,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
text = (char *)xmpp_alloc(stanza->ctx, len + 1);
|
||||
text = (char *)strophe_alloc(stanza->ctx, len + 1);
|
||||
if (!text)
|
||||
return NULL;
|
||||
|
||||
@@ -1027,7 +1093,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *const stanza)
|
||||
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *stanza)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TEXT)
|
||||
return stanza->data;
|
||||
@@ -1046,7 +1112,7 @@ const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *const stanza)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_id(xmpp_stanza_t *const stanza, const char *const id)
|
||||
int xmpp_stanza_set_id(xmpp_stanza_t *stanza, const char *id)
|
||||
{
|
||||
return xmpp_stanza_set_attribute(stanza, "id", id);
|
||||
}
|
||||
@@ -1062,7 +1128,7 @@ int xmpp_stanza_set_id(xmpp_stanza_t *const stanza, const char *const id)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_type(xmpp_stanza_t *const stanza, const char *const type)
|
||||
int xmpp_stanza_set_type(xmpp_stanza_t *stanza, const char *type)
|
||||
{
|
||||
return xmpp_stanza_set_attribute(stanza, "type", type);
|
||||
}
|
||||
@@ -1079,7 +1145,7 @@ int xmpp_stanza_set_type(xmpp_stanza_t *const stanza, const char *const type)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_to(xmpp_stanza_t *const stanza, const char *const to)
|
||||
int xmpp_stanza_set_to(xmpp_stanza_t *stanza, const char *to)
|
||||
{
|
||||
return xmpp_stanza_set_attribute(stanza, "to", to);
|
||||
}
|
||||
@@ -1096,7 +1162,7 @@ int xmpp_stanza_set_to(xmpp_stanza_t *const stanza, const char *const to)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_set_from(xmpp_stanza_t *const stanza, const char *const from)
|
||||
int xmpp_stanza_set_from(xmpp_stanza_t *stanza, const char *from)
|
||||
{
|
||||
return xmpp_stanza_set_attribute(stanza, "from", from);
|
||||
}
|
||||
@@ -1112,8 +1178,7 @@ int xmpp_stanza_set_from(xmpp_stanza_t *const stanza, const char *const from)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza,
|
||||
const char *const name)
|
||||
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *stanza, const char *name)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
@@ -1133,8 +1198,7 @@ const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
|
||||
const char *const name)
|
||||
int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return -1;
|
||||
@@ -1146,8 +1210,8 @@ int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
|
||||
}
|
||||
|
||||
/** Create a stanza object in reply to another.
|
||||
* This function makes a copy of a stanza object with the attribute “to” set
|
||||
* its original “from”.
|
||||
* This function makes a copy of a stanza object with the attribute "to" set
|
||||
* its original "from".
|
||||
* The stanza will have a reference count of one, so the caller does not
|
||||
* need to clone it.
|
||||
*
|
||||
@@ -1157,7 +1221,7 @@ int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza)
|
||||
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *stanza)
|
||||
{
|
||||
xmpp_stanza_t *copy = NULL;
|
||||
const char *from;
|
||||
@@ -1174,7 +1238,7 @@ xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza)
|
||||
copy->type = stanza->type;
|
||||
|
||||
if (stanza->data) {
|
||||
copy->data = xmpp_strdup(stanza->ctx, stanza->data);
|
||||
copy->data = strophe_strdup(stanza->ctx, stanza->data);
|
||||
if (!copy->data)
|
||||
goto copy_error;
|
||||
}
|
||||
@@ -1204,7 +1268,7 @@ copy_error:
|
||||
* Check https://tools.ietf.org/html/rfc6120#section-8.3 for details.
|
||||
*
|
||||
* @param stanza a Strophe stanza object
|
||||
* @param error_type type attribute in the <error/> child element
|
||||
* @param error_type type attribute in the `<error/>` child element
|
||||
* @param condition the defined-condition (e.g. "item-not-found")
|
||||
* @param text optional description, may be NULL
|
||||
*
|
||||
@@ -1212,16 +1276,16 @@ copy_error:
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
|
||||
const char *const error_type,
|
||||
const char *const condition,
|
||||
const char *const text)
|
||||
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *stanza,
|
||||
const char *error_type,
|
||||
const char *condition,
|
||||
const char *text)
|
||||
{
|
||||
xmpp_ctx_t *ctx = stanza->ctx;
|
||||
xmpp_stanza_t *reply = NULL;
|
||||
xmpp_stanza_t *error;
|
||||
xmpp_stanza_t *item;
|
||||
xmpp_stanza_t *text_stanza;
|
||||
xmpp_stanza_t *error = NULL;
|
||||
xmpp_stanza_t *item = NULL;
|
||||
xmpp_stanza_t *text_stanza = NULL;
|
||||
const char *to;
|
||||
|
||||
if (!error_type || !condition)
|
||||
@@ -1230,41 +1294,53 @@ xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
|
||||
reply = xmpp_stanza_reply(stanza);
|
||||
if (!reply)
|
||||
goto quit_err;
|
||||
|
||||
xmpp_stanza_set_type(reply, "error");
|
||||
if (xmpp_stanza_set_type(reply, "error") != XMPP_EOK)
|
||||
goto quit_err;
|
||||
to = xmpp_stanza_get_to(stanza);
|
||||
if (to)
|
||||
xmpp_stanza_set_from(reply, to);
|
||||
if (xmpp_stanza_set_from(reply, to) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
|
||||
error = xmpp_stanza_new(ctx);
|
||||
if (!error)
|
||||
goto quit_err;
|
||||
xmpp_stanza_set_name(error, "error");
|
||||
xmpp_stanza_set_type(error, error_type);
|
||||
xmpp_stanza_add_child(reply, error);
|
||||
if (xmpp_stanza_set_name(error, "error") != XMPP_EOK)
|
||||
goto quit_err;
|
||||
if (xmpp_stanza_set_type(error, error_type) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
if (xmpp_stanza_add_child(reply, error) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
xmpp_stanza_release(error);
|
||||
|
||||
item = xmpp_stanza_new(ctx);
|
||||
if (!item)
|
||||
goto quit_err;
|
||||
xmpp_stanza_set_name(item, condition);
|
||||
xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF);
|
||||
xmpp_stanza_add_child(error, item);
|
||||
if (xmpp_stanza_set_name(item, condition) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
xmpp_stanza_release(item);
|
||||
|
||||
if (text) {
|
||||
item = xmpp_stanza_new(ctx);
|
||||
if (!item)
|
||||
goto quit_err;
|
||||
xmpp_stanza_set_name(item, "text");
|
||||
xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF);
|
||||
xmpp_stanza_add_child(error, item);
|
||||
if (xmpp_stanza_set_name(item, "text") != XMPP_EOK)
|
||||
goto quit_err;
|
||||
if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
xmpp_stanza_release(item);
|
||||
text_stanza = xmpp_stanza_new(ctx);
|
||||
if (!text_stanza)
|
||||
goto quit_err;
|
||||
xmpp_stanza_set_text(text_stanza, text);
|
||||
xmpp_stanza_add_child(item, text_stanza);
|
||||
if (xmpp_stanza_set_text(text_stanza, text) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
if (xmpp_stanza_add_child(item, text_stanza) != XMPP_EOK)
|
||||
goto quit_err;
|
||||
xmpp_stanza_release(text_stanza);
|
||||
}
|
||||
|
||||
@@ -1273,14 +1349,20 @@ xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
|
||||
quit_err:
|
||||
if (reply)
|
||||
xmpp_stanza_release(reply);
|
||||
if (error)
|
||||
xmpp_stanza_release(error);
|
||||
if (item)
|
||||
xmpp_stanza_release(item);
|
||||
if (text_stanza)
|
||||
xmpp_stanza_release(text_stanza);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
|
||||
const char *const name,
|
||||
const char *const type,
|
||||
const char *const id,
|
||||
const char *const to)
|
||||
const char *name,
|
||||
const char *type,
|
||||
const char *id,
|
||||
const char *to)
|
||||
{
|
||||
xmpp_stanza_t *stanza = xmpp_stanza_new(ctx);
|
||||
int ret;
|
||||
@@ -1301,7 +1383,7 @@ static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
|
||||
return stanza;
|
||||
}
|
||||
|
||||
/** Create a <message/> stanza object with given attributes.
|
||||
/** Create a `<message/>` stanza object with given attributes.
|
||||
* Attributes are optional and may be NULL.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
@@ -1314,20 +1396,20 @@ static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
|
||||
const char *const type,
|
||||
const char *const to,
|
||||
const char *const id)
|
||||
const char *type,
|
||||
const char *to,
|
||||
const char *id)
|
||||
{
|
||||
return _stanza_new_with_attrs(ctx, "message", type, id, to);
|
||||
}
|
||||
|
||||
/** Get text from <body/> child element.
|
||||
/** Get text from `<body/>` child element.
|
||||
* This function returns new allocated string. The caller is responsible
|
||||
* for freeing this string with xmpp_free().
|
||||
*
|
||||
* @param msg well formed <message/> stanza
|
||||
* @param msg well formed `<message/>` stanza
|
||||
*
|
||||
* @return allocated string or NULL on failure (no <body/> element or
|
||||
* @return allocated string or NULL on failure (no `<body/>` element or
|
||||
* memory allocation error)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
@@ -1346,16 +1428,17 @@ char *xmpp_message_get_body(xmpp_stanza_t *msg)
|
||||
return text;
|
||||
}
|
||||
|
||||
/** Add <body/> child element to a <message/> stanza with the given text.
|
||||
/** Add `<body/>` child element to a `<message/>` stanza with the given text.
|
||||
*
|
||||
* @param msg a <message> stanza object without <body/> child element.
|
||||
* @param msg a `<message>` stanza object without `<body/>` child element.
|
||||
* @param text The text that shall be placed in the body.
|
||||
*
|
||||
* @return 0 on success (XMPP_EOK), and a number less than 0 on failure
|
||||
* (XMPP_EMEM, XMPP_EINVOP)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
|
||||
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *text)
|
||||
{
|
||||
xmpp_ctx_t *ctx = msg->ctx;
|
||||
xmpp_stanza_t *body;
|
||||
@@ -1363,7 +1446,7 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
/* check that msg is a <message/> stanza and doesn't contain <body/> */
|
||||
/* check that msg is a `<message/>` stanza and doesn't contain `<body/>` */
|
||||
name = xmpp_stanza_get_name(msg);
|
||||
body = xmpp_stanza_get_child_by_name(msg, "body");
|
||||
if (!name || strcmp(name, "message") != 0 || body)
|
||||
@@ -1390,7 +1473,7 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Create an <iq/> stanza object with given attributes.
|
||||
/** Create an `<iq/>` stanza object with given attributes.
|
||||
* Attributes are optional and may be NULL.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
@@ -1401,13 +1484,12 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *
|
||||
xmpp_iq_new(xmpp_ctx_t *ctx, const char *const type, const char *const id)
|
||||
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char *type, const char *id)
|
||||
{
|
||||
return _stanza_new_with_attrs(ctx, "iq", type, id, NULL);
|
||||
}
|
||||
|
||||
/** Create a <presence/> stanza object.
|
||||
/** Create a `<presence/>` stanza object.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
*
|
||||
@@ -1424,7 +1506,7 @@ xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx)
|
||||
* The error text is optional and may be NULL.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param type enum of xmpp_error_type_t
|
||||
* @param type enum of strophe_error_type_t
|
||||
* @param text content of a 'text'
|
||||
*
|
||||
* @return a new Strophe stanza object
|
||||
@@ -1433,9 +1515,8 @@ xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx)
|
||||
*
|
||||
* @ingroup Stanza
|
||||
*/
|
||||
xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
|
||||
xmpp_error_type_t const type,
|
||||
const char *const text)
|
||||
xmpp_stanza_t *
|
||||
xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text)
|
||||
{
|
||||
xmpp_stanza_t *error =
|
||||
_stanza_new_with_attrs(ctx, "stream:error", NULL, NULL, NULL);
|
||||
@@ -1520,8 +1601,7 @@ xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
|
||||
}
|
||||
|
||||
xmpp_stanza_set_ns(error_type, XMPP_NS_STREAMS_IETF);
|
||||
xmpp_stanza_add_child(error, error_type);
|
||||
xmpp_stanza_release(error_type);
|
||||
xmpp_stanza_add_child_ex(error, error_type, 0);
|
||||
|
||||
if (text) {
|
||||
xmpp_stanza_t *error_text = xmpp_stanza_new(ctx);
|
||||
@@ -1531,11 +1611,9 @@ xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
|
||||
xmpp_stanza_set_ns(error_text, XMPP_NS_STREAMS_IETF);
|
||||
|
||||
xmpp_stanza_set_text(content, text);
|
||||
xmpp_stanza_add_child(error_text, content);
|
||||
xmpp_stanza_release(content);
|
||||
xmpp_stanza_add_child_ex(error_text, content, 0);
|
||||
|
||||
xmpp_stanza_add_child(error, error_text);
|
||||
xmpp_stanza_release(error_text);
|
||||
xmpp_stanza_add_child_ex(error, error_text, 0);
|
||||
}
|
||||
|
||||
return error;
|
||||
@@ -1556,8 +1634,11 @@ static void _stub_stream_end(char *name, void *userdata)
|
||||
|
||||
static void _stream_stanza(xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
stanza = xmpp_stanza_clone(stanza);
|
||||
*(xmpp_stanza_t **)userdata = stanza;
|
||||
xmpp_stanza_t **dest = userdata;
|
||||
if (*dest == NULL) {
|
||||
stanza = xmpp_stanza_clone(stanza);
|
||||
*dest = stanza;
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a stanza object from the string.
|
||||
|
||||
266
src/tls.c
Normal file
266
src/tls.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* tls.c
|
||||
** strophe XMPP client library -- generic TLS functions
|
||||
**
|
||||
** Copyright (C) 2021 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Generic TLS functionality.
|
||||
*/
|
||||
|
||||
/** @defgroup TLS SSL/TLS specific functionality
|
||||
* These functions provide SSL/TLS specific functionality.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "strophe.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
const struct conn_interface tls_intf = {
|
||||
tls_read,
|
||||
tls_write,
|
||||
tls_clear_pending_write,
|
||||
tls_pending,
|
||||
tls_error,
|
||||
tls_is_recoverable,
|
||||
/* init conn */
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct _dnsname_t {
|
||||
char **data;
|
||||
size_t cur, max;
|
||||
};
|
||||
|
||||
const size_t tlscert_dnsnames_increment = 4;
|
||||
|
||||
/** Get the Strophe context which is assigned to this certificate.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @return the Strophe context object where this certificate originates from
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert)
|
||||
{
|
||||
return cert->ctx;
|
||||
}
|
||||
|
||||
/** Get the Strophe connection which is assigned to this certificate.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @return the Strophe connection object where this certificate originates from
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert)
|
||||
{
|
||||
return cert->conn;
|
||||
}
|
||||
|
||||
/** Get the complete PEM of this certificate.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @return a string containing the PEM of this certificate
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert)
|
||||
{
|
||||
return cert->pem;
|
||||
}
|
||||
|
||||
/** Get the dnsName entries out of the SubjectAlternativeNames.
|
||||
*
|
||||
* Note: Max. `MAX_NUM_DNSNAMES` are supported.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
* @param n which dnsName entry
|
||||
*
|
||||
* @return a string with the n'th dnsName
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n)
|
||||
{
|
||||
if (n >= cert->dnsnames->cur)
|
||||
return NULL;
|
||||
return cert->dnsnames->data[n];
|
||||
}
|
||||
|
||||
/** Get various parts of the certificate as String.
|
||||
*
|
||||
* c.f. \ref xmpp_cert_element_t for details.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
* @param elmnt which part of the certificate
|
||||
*
|
||||
* @return a string with the part of the certificate
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
|
||||
xmpp_cert_element_t elmnt)
|
||||
{
|
||||
if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
|
||||
return NULL;
|
||||
return cert->elements[elmnt];
|
||||
}
|
||||
|
||||
/** Get a descriptive string for each xmpp_cert_element_t.
|
||||
*
|
||||
* c.f. \ref xmpp_cert_element_t for details.
|
||||
*
|
||||
* @param elmnt which element
|
||||
*
|
||||
* @return a string with the description
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)
|
||||
{
|
||||
static const char *descriptions[] = {
|
||||
"X.509 Version",
|
||||
"SerialNumber",
|
||||
"Subject",
|
||||
"Issuer",
|
||||
"Issued On",
|
||||
"Expires On",
|
||||
"Public Key Algorithm",
|
||||
"Certificate Signature Algorithm",
|
||||
"Fingerprint SHA-1",
|
||||
"Fingerprint SHA-256",
|
||||
};
|
||||
if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
|
||||
return NULL;
|
||||
return descriptions[elmnt];
|
||||
}
|
||||
|
||||
/** Allocate and initialize a Strophe TLS certificate object.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
*
|
||||
* @return a certificate object or NULL
|
||||
*/
|
||||
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_tlscert_t *tlscert = strophe_alloc(ctx, sizeof(*tlscert));
|
||||
if (!tlscert)
|
||||
return NULL;
|
||||
memset(tlscert, 0, sizeof(*tlscert));
|
||||
|
||||
tlscert->dnsnames = strophe_alloc(ctx, sizeof(*tlscert->dnsnames));
|
||||
if (!tlscert->dnsnames) {
|
||||
strophe_free(ctx, tlscert);
|
||||
return NULL;
|
||||
}
|
||||
memset(tlscert->dnsnames, 0, sizeof(*tlscert->dnsnames));
|
||||
|
||||
tlscert->ctx = ctx;
|
||||
|
||||
return tlscert;
|
||||
}
|
||||
|
||||
/** Free a certificate object.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
void xmpp_tlscert_free(xmpp_tlscert_t *cert)
|
||||
{
|
||||
size_t n;
|
||||
for (n = 0; n < ARRAY_SIZE(cert->elements); ++n) {
|
||||
if (cert->elements[n])
|
||||
strophe_free(cert->ctx, cert->elements[n]);
|
||||
}
|
||||
if (cert->dnsnames->data) {
|
||||
for (n = 0; n < cert->dnsnames->cur; ++n) {
|
||||
if (cert->dnsnames->data[n])
|
||||
strophe_free(cert->ctx, cert->dnsnames->data[n]);
|
||||
}
|
||||
}
|
||||
strophe_free(cert->ctx, cert->dnsnames->data);
|
||||
strophe_free(cert->ctx, cert->dnsnames);
|
||||
if (cert->pem)
|
||||
strophe_free(cert->ctx, cert->pem);
|
||||
strophe_free(cert->ctx, cert);
|
||||
}
|
||||
|
||||
/** Add a dnsName to the Strophe TLS certificate object.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
* @param dnsname dnsName that shall be stored
|
||||
*
|
||||
* @return classic Unix style - 0=success, 1=error
|
||||
*/
|
||||
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname)
|
||||
{
|
||||
if ((cert->dnsnames->cur + 1) >= cert->dnsnames->max) {
|
||||
char **dnsnames =
|
||||
strophe_realloc(cert->ctx, cert->dnsnames->data,
|
||||
(cert->dnsnames->max + tlscert_dnsnames_increment) *
|
||||
sizeof(char **));
|
||||
if (!dnsnames)
|
||||
return 1;
|
||||
cert->dnsnames->data = dnsnames;
|
||||
cert->dnsnames->max += tlscert_dnsnames_increment;
|
||||
}
|
||||
cert->dnsnames->data[cert->dnsnames->cur++] =
|
||||
strophe_strdup(cert->ctx, dnsname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls_caching_password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn)
|
||||
{
|
||||
int ret;
|
||||
unsigned char hash[XMPP_SHA1_DIGEST_SIZE];
|
||||
|
||||
const char *fname = conn->tls_client_cert;
|
||||
size_t fname_len = strlen(fname);
|
||||
xmpp_sha1_digest((void *)fname, fname_len, hash);
|
||||
if (fname_len && fname_len == conn->password_cache.fnamelen &&
|
||||
memcmp(hash, conn->password_cache.fname_hash, sizeof(hash)) == 0) {
|
||||
if (conn->password_cache.passlen) {
|
||||
memcpy(pw, conn->password_cache.pass,
|
||||
conn->password_cache.passlen + 1);
|
||||
return conn->password_cache.passlen;
|
||||
}
|
||||
}
|
||||
size_t max_len = pw_max == 256 ? pw_max : sizeof(conn->password_cache.pass);
|
||||
ret = conn->password_callback(conn->password_cache.pass, max_len, conn,
|
||||
conn->password_callback_userdata);
|
||||
|
||||
if (ret < 0 || ret >= (ssize_t)max_len) {
|
||||
memset(conn->password_cache.pass, 0, sizeof(conn->password_cache.pass));
|
||||
return -1;
|
||||
}
|
||||
conn->password_cache.pass[ret] = '\0';
|
||||
memcpy(pw, conn->password_cache.pass, ret + 1);
|
||||
conn->password_cache.passlen = ret;
|
||||
conn->password_cache.fnamelen = fname_len;
|
||||
memcpy(conn->password_cache.fname_hash, hash, sizeof(hash));
|
||||
return conn->password_cache.passlen;
|
||||
}
|
||||
|
||||
void tls_clear_password_cache(xmpp_conn_t *conn)
|
||||
{
|
||||
memset(&conn->password_cache, 0, sizeof(conn->password_cache));
|
||||
}
|
||||
43
src/tls.h
43
src/tls.h
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* tls.h
|
||||
** strophe XMPP client library -- TLS abstraction header
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -21,24 +22,52 @@
|
||||
|
||||
typedef struct _tls tls_t;
|
||||
|
||||
typedef struct _dnsname_t dnsname_t;
|
||||
|
||||
struct _xmpp_tlscert_t {
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
char *pem;
|
||||
char *elements[XMPP_CERT_ELEMENT_MAX];
|
||||
dnsname_t *dnsnames;
|
||||
};
|
||||
|
||||
/* provided by the real TLS implementation */
|
||||
|
||||
void tls_initialize(void);
|
||||
void tls_shutdown(void);
|
||||
|
||||
tls_t *tls_new(xmpp_conn_t *conn);
|
||||
void tls_free(tls_t *tls);
|
||||
|
||||
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n);
|
||||
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn);
|
||||
|
||||
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn);
|
||||
int tls_set_credentials(tls_t *tls, const char *cafilename);
|
||||
int tls_init_channel_binding(tls_t *tls,
|
||||
const char **binding_prefix,
|
||||
size_t *binding_prefix_len);
|
||||
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size);
|
||||
|
||||
int tls_start(tls_t *tls);
|
||||
int tls_stop(tls_t *tls);
|
||||
|
||||
int tls_error(tls_t *tls);
|
||||
int tls_pending(struct conn_interface *intf);
|
||||
int tls_read(struct conn_interface *intf, void *buff, size_t len);
|
||||
int tls_write(struct conn_interface *intf, const void *buff, size_t len);
|
||||
int tls_clear_pending_write(struct conn_interface *intf);
|
||||
|
||||
int tls_pending(tls_t *tls);
|
||||
int tls_read(tls_t *tls, void *const buff, const size_t len);
|
||||
int tls_write(tls_t *tls, const void *const buff, const size_t len);
|
||||
int tls_error(struct conn_interface *intf);
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error);
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls);
|
||||
int tls_is_recoverable(int error);
|
||||
/* provided by tls.c */
|
||||
extern const struct conn_interface tls_intf;
|
||||
|
||||
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx);
|
||||
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname);
|
||||
|
||||
int tls_caching_password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn);
|
||||
void tls_clear_password_cache(xmpp_conn_t *conn);
|
||||
|
||||
#endif /* __LIBSTROPHE_TLS_H__ */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* tls_dummy.c
|
||||
** strophe XMPP client library -- TLS abstraction dummy impl.
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -33,6 +34,21 @@ void tls_shutdown(void)
|
||||
return;
|
||||
}
|
||||
|
||||
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
|
||||
{
|
||||
UNUSED(conn);
|
||||
UNUSED(n);
|
||||
/* always fail */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
|
||||
{
|
||||
UNUSED(conn);
|
||||
/* always fail */
|
||||
return 0;
|
||||
}
|
||||
|
||||
tls_t *tls_new(xmpp_conn_t *conn)
|
||||
{
|
||||
UNUSED(conn);
|
||||
@@ -46,6 +62,12 @@ void tls_free(tls_t *tls)
|
||||
return;
|
||||
}
|
||||
|
||||
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
|
||||
{
|
||||
UNUSED(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||
{
|
||||
UNUSED(tls);
|
||||
@@ -53,6 +75,23 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_init_channel_binding(tls_t *tls,
|
||||
const char **binding_prefix,
|
||||
size_t *binding_prefix_len)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(binding_prefix);
|
||||
UNUSED(binding_prefix_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tls_start(tls_t *tls)
|
||||
{
|
||||
UNUSED(tls);
|
||||
@@ -65,43 +104,43 @@ int tls_stop(tls_t *tls)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_error(tls_t *tls)
|
||||
int tls_error(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
/* todo: some kind of error polling/dump */
|
||||
UNUSED(intf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls_pending(tls_t *tls)
|
||||
int tls_pending(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
UNUSED(buff);
|
||||
UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_write(tls_t *tls, const void *const buff, const size_t len)
|
||||
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
UNUSED(buff);
|
||||
UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls)
|
||||
int tls_clear_pending_write(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_is_recoverable(int error)
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||
{
|
||||
UNUSED(intf);
|
||||
UNUSED(error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
575
src/tls_gnutls.c
575
src/tls_gnutls.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* tls.c
|
||||
** strophe XMPP client library -- TLS abstraction header
|
||||
**
|
||||
@@ -6,27 +7,44 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* TLS implementation with GNUTLS
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <gnutls/x509-ext.h>
|
||||
#include <gnutls/pkcs11.h>
|
||||
#include <gnutls/pkcs12.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "tls.h"
|
||||
#include "sock.h"
|
||||
|
||||
/* FIXME this shouldn't be a constant string */
|
||||
#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030702
|
||||
/* This is a very dirty workaround to make this file compile on older GnuTLS
|
||||
* We simply define our own value for the later introduced
|
||||
* `GNUTLS_CB_TLS_EXPORTER` which makes it compile, but it will fail on runtime
|
||||
* when the combination of the following options happen:
|
||||
* * GnuTLS < 3.7.2
|
||||
* * TLS 1.3
|
||||
* * A SCRAM PLUS variant to authenticate
|
||||
* We will think about fixing this once a user pops up who has that problem.
|
||||
*/
|
||||
#define GNUTLS_CB_TLS_EXPORTER 3
|
||||
#endif
|
||||
|
||||
struct _tls {
|
||||
xmpp_ctx_t *ctx; /* do we need this? */
|
||||
sock_t sock;
|
||||
xmpp_conn_t *conn;
|
||||
gnutls_session_t session;
|
||||
gnutls_certificate_credentials_t cred;
|
||||
gnutls_x509_crt_t client_cert;
|
||||
gnutls_datum_t channel_binding;
|
||||
int lasterror;
|
||||
};
|
||||
|
||||
@@ -46,41 +64,502 @@ void tls_shutdown(void)
|
||||
gnutls_global_deinit();
|
||||
}
|
||||
|
||||
static int _tls_password_callback(void *userdata,
|
||||
int attempt,
|
||||
const char *token_url,
|
||||
const char *token_label,
|
||||
unsigned int flags,
|
||||
char *pin,
|
||||
size_t pin_max)
|
||||
{
|
||||
xmpp_conn_t *conn = userdata;
|
||||
UNUSED(attempt);
|
||||
UNUSED(token_url);
|
||||
UNUSED(token_label);
|
||||
UNUSED(flags);
|
||||
int ret = tls_caching_password_callback(pin, pin_max, conn);
|
||||
return ret > 0 ? 0 : GNUTLS_E_PKCS11_PIN_ERROR;
|
||||
}
|
||||
|
||||
static gnutls_x509_crt_t _tls_load_cert_x509(xmpp_conn_t *conn)
|
||||
{
|
||||
gnutls_x509_crt_t cert;
|
||||
gnutls_datum_t data;
|
||||
int res;
|
||||
if (gnutls_x509_crt_init(&cert) < 0)
|
||||
return NULL;
|
||||
if (gnutls_load_file(conn->tls_client_cert, &data) < 0)
|
||||
goto error_out;
|
||||
res = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM);
|
||||
gnutls_free(data.data);
|
||||
if (res < 0)
|
||||
goto error_out;
|
||||
return cert;
|
||||
error_out:
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gnutls_x509_crt_t _tls_load_cert_p12(xmpp_conn_t *conn)
|
||||
{
|
||||
gnutls_pkcs12_t p12;
|
||||
gnutls_x509_crt_t *cert = NULL;
|
||||
gnutls_datum_t data;
|
||||
gnutls_x509_privkey_t key;
|
||||
unsigned int cert_num = 0, retries = 0;
|
||||
int err = -1;
|
||||
if (gnutls_pkcs12_init(&p12) < 0)
|
||||
return NULL;
|
||||
if (gnutls_load_file(conn->tls_client_cert, &data) < 0)
|
||||
goto error_out;
|
||||
if (gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0) < 0)
|
||||
goto error_out2;
|
||||
|
||||
/* First try to open file with no pass */
|
||||
if ((err = gnutls_pkcs12_simple_parse(p12, NULL, &key, &cert, &cert_num,
|
||||
NULL, NULL, NULL, 0)) == 0) {
|
||||
goto done;
|
||||
}
|
||||
/* Now let's try to open file with an empty pass */
|
||||
if ((err = gnutls_pkcs12_simple_parse(p12, "", &key, &cert, &cert_num, NULL,
|
||||
NULL, NULL, 0)) == 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!conn->password_callback) {
|
||||
strophe_error(conn->ctx, "tls", "No password callback set");
|
||||
goto error_out2;
|
||||
}
|
||||
/* ... and only now ask the user for a password */
|
||||
while (retries++ < conn->password_retries) {
|
||||
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
|
||||
int passlen =
|
||||
_tls_password_callback(conn, 0, NULL, NULL, 0, pass, sizeof(pass));
|
||||
if (passlen < 0)
|
||||
continue;
|
||||
|
||||
err = gnutls_pkcs12_simple_parse(p12, pass, &key, &cert, &cert_num,
|
||||
NULL, NULL, NULL, 0);
|
||||
memset(pass, 0, sizeof(pass));
|
||||
if (err == 0)
|
||||
break;
|
||||
tls_clear_password_cache(conn);
|
||||
if (err != GNUTLS_E_DECRYPTION_FAILED &&
|
||||
err != GNUTLS_E_MAC_VERIFY_FAILED) {
|
||||
strophe_error(conn->ctx, "tls", "could not read P12 file");
|
||||
break;
|
||||
}
|
||||
strophe_debug(conn->ctx, "tls", "wrong password?");
|
||||
}
|
||||
|
||||
done:
|
||||
gnutls_pkcs12_deinit(p12);
|
||||
gnutls_free(data.data);
|
||||
if (err < 0)
|
||||
goto error_out;
|
||||
gnutls_x509_privkey_deinit(key);
|
||||
if (cert_num > 1) {
|
||||
strophe_error(conn->ctx, "tls", "Can't handle stack of %u certs",
|
||||
cert_num);
|
||||
goto error_out;
|
||||
}
|
||||
gnutls_x509_crt_t ret = *cert;
|
||||
gnutls_free(cert);
|
||||
return ret;
|
||||
error_out2:
|
||||
gnutls_free(data.data);
|
||||
error_out:
|
||||
tls_clear_password_cache(conn);
|
||||
if (cert) {
|
||||
for (unsigned int n = 0; n < cert_num; ++n) {
|
||||
gnutls_x509_crt_deinit(cert[n]);
|
||||
}
|
||||
gnutls_free(cert);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gnutls_x509_crt_t _tls_load_cert(xmpp_conn_t *conn)
|
||||
{
|
||||
if (conn->tls && conn->tls->client_cert)
|
||||
return conn->tls->client_cert;
|
||||
if (conn->tls_client_cert && !conn->tls_client_key) {
|
||||
return _tls_load_cert_p12(conn);
|
||||
}
|
||||
return _tls_load_cert_x509(conn);
|
||||
}
|
||||
|
||||
static void _tls_free_cert(xmpp_conn_t *conn, gnutls_x509_crt_t cert)
|
||||
{
|
||||
if (conn->tls && conn->tls->client_cert == cert)
|
||||
return;
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
}
|
||||
|
||||
static int _tls_get_id_on_xmppaddr(xmpp_conn_t *conn,
|
||||
gnutls_x509_crt_t cert,
|
||||
unsigned int n,
|
||||
char **ret)
|
||||
{
|
||||
gnutls_datum_t san;
|
||||
size_t name_len, oid_len;
|
||||
char oid[128], name[128];
|
||||
name_len = oid_len = 128;
|
||||
int res =
|
||||
gnutls_x509_crt_get_subject_alt_name(cert, n, name, &name_len, NULL);
|
||||
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||
/* This is the happy flow path with fixed GnuTLS.
|
||||
* While implementing this I stumbled over an issue in GnuTLS
|
||||
* which lead to
|
||||
* https://gitlab.com/gnutls/gnutls/-/merge_requests/1397
|
||||
*/
|
||||
if (ret) {
|
||||
*ret = strophe_strdup(conn->ctx, name);
|
||||
}
|
||||
return GNUTLS_SAN_OTHERNAME_XMPP;
|
||||
}
|
||||
if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
|
||||
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
||||
if (res != GNUTLS_SAN_OTHERNAME)
|
||||
return GNUTLS_E_X509_UNKNOWN_SAN;
|
||||
res = gnutls_x509_crt_get_subject_alt_othername_oid(cert, n, oid, &oid_len);
|
||||
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||
gnutls_datum_t xmpp_addr;
|
||||
san.data = (unsigned char *)name;
|
||||
san.size = name_len;
|
||||
res = gnutls_x509_othername_to_virtual(oid, &san, NULL, &xmpp_addr);
|
||||
if (res < 0) {
|
||||
gnutls_free(xmpp_addr.data);
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
if (ret) {
|
||||
*ret = strophe_strdup(conn->ctx, (char *)xmpp_addr.data);
|
||||
}
|
||||
gnutls_free(xmpp_addr.data);
|
||||
return GNUTLS_SAN_OTHERNAME_XMPP;
|
||||
}
|
||||
return GNUTLS_E_X509_UNKNOWN_SAN;
|
||||
}
|
||||
|
||||
static int _tls_id_on_xmppaddr(xmpp_conn_t *conn,
|
||||
gnutls_x509_crt_t cert,
|
||||
unsigned int n,
|
||||
char **ret)
|
||||
{
|
||||
int res = GNUTLS_E_SUCCESS;
|
||||
unsigned int i, j;
|
||||
for (i = j = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++i) {
|
||||
res = _tls_get_id_on_xmppaddr(conn, cert, i, NULL);
|
||||
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||
if (j == n) {
|
||||
res = _tls_get_id_on_xmppaddr(conn, cert, i, ret);
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Search through the SubjectAlternativeNames and return the next
|
||||
* id-on-xmppAddr element starting from `n`.
|
||||
*/
|
||||
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
|
||||
{
|
||||
char *ret = NULL;
|
||||
gnutls_x509_crt_t cert = _tls_load_cert(conn);
|
||||
if (cert == NULL)
|
||||
return NULL;
|
||||
_tls_id_on_xmppaddr(conn, cert, n, &ret);
|
||||
_tls_free_cert(conn, cert);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
|
||||
{
|
||||
unsigned int ret = 0, n;
|
||||
int res = GNUTLS_E_SUCCESS;
|
||||
gnutls_x509_crt_t cert = _tls_load_cert(conn);
|
||||
if (cert == NULL)
|
||||
return 0;
|
||||
for (n = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++n) {
|
||||
res = _tls_id_on_xmppaddr(conn, cert, n, NULL);
|
||||
if (res == GNUTLS_SAN_OTHERNAME_XMPP)
|
||||
ret++;
|
||||
}
|
||||
_tls_free_cert(conn, cert);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmpp_tlscert_t *_x509_to_tlscert(xmpp_ctx_t *ctx, gnutls_x509_crt_t cert)
|
||||
{
|
||||
int res;
|
||||
char buf[512], smallbuf[64];
|
||||
size_t size, m;
|
||||
unsigned int algo, n;
|
||||
gnutls_datum_t data;
|
||||
time_t time_val;
|
||||
xmpp_tlscert_t *tlscert = tlscert_new(ctx);
|
||||
|
||||
gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &data);
|
||||
tlscert->pem = strophe_alloc(ctx, data.size + 1);
|
||||
memcpy(tlscert->pem, data.data, data.size);
|
||||
tlscert->pem[data.size] = '\0';
|
||||
gnutls_free(data.data);
|
||||
|
||||
size = sizeof(buf);
|
||||
gnutls_x509_crt_get_dn(cert, buf, &size);
|
||||
tlscert->elements[XMPP_CERT_SUBJECT] = strophe_strdup(ctx, buf);
|
||||
size = sizeof(buf);
|
||||
gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
|
||||
tlscert->elements[XMPP_CERT_ISSUER] = strophe_strdup(ctx, buf);
|
||||
|
||||
time_val = gnutls_x509_crt_get_activation_time(cert);
|
||||
tlscert->elements[XMPP_CERT_NOTBEFORE] =
|
||||
strophe_strdup(ctx, ctime(&time_val));
|
||||
tlscert->elements[XMPP_CERT_NOTBEFORE]
|
||||
[strlen(tlscert->elements[XMPP_CERT_NOTBEFORE]) - 1] =
|
||||
'\0';
|
||||
time_val = gnutls_x509_crt_get_expiration_time(cert);
|
||||
tlscert->elements[XMPP_CERT_NOTAFTER] =
|
||||
strophe_strdup(ctx, ctime(&time_val));
|
||||
tlscert->elements[XMPP_CERT_NOTAFTER]
|
||||
[strlen(tlscert->elements[XMPP_CERT_NOTAFTER]) - 1] = '\0';
|
||||
|
||||
size = sizeof(smallbuf);
|
||||
gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, smallbuf, &size);
|
||||
hex_encode(buf, smallbuf, size);
|
||||
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA1] = strophe_strdup(ctx, buf);
|
||||
size = sizeof(smallbuf);
|
||||
gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, smallbuf, &size);
|
||||
hex_encode(buf, smallbuf, size);
|
||||
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA256] = strophe_strdup(ctx, buf);
|
||||
|
||||
strophe_snprintf(buf, sizeof(buf), "%d", gnutls_x509_crt_get_version(cert));
|
||||
tlscert->elements[XMPP_CERT_VERSION] = strophe_strdup(ctx, buf);
|
||||
|
||||
algo = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
|
||||
tlscert->elements[XMPP_CERT_KEYALG] =
|
||||
strophe_strdup(ctx, gnutls_pk_algorithm_get_name(algo));
|
||||
algo = gnutls_x509_crt_get_signature_algorithm(cert);
|
||||
tlscert->elements[XMPP_CERT_SIGALG] =
|
||||
strophe_strdup(ctx, gnutls_sign_get_name(algo));
|
||||
|
||||
size = sizeof(smallbuf);
|
||||
gnutls_x509_crt_get_serial(cert, smallbuf, &size);
|
||||
hex_encode(buf, smallbuf, size);
|
||||
tlscert->elements[XMPP_CERT_SERIALNUMBER] = strophe_strdup(ctx, buf);
|
||||
|
||||
for (n = 0, m = 0, res = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
||||
++n) {
|
||||
size = sizeof(buf);
|
||||
res = gnutls_x509_crt_get_subject_alt_name(cert, n, buf, &size, NULL);
|
||||
if (res == GNUTLS_SAN_DNSNAME) {
|
||||
if (tlscert_add_dnsname(tlscert, buf))
|
||||
strophe_debug(ctx, "tls", "Can't store dnsName(%zu): %s", m,
|
||||
buf);
|
||||
m++;
|
||||
}
|
||||
}
|
||||
|
||||
return tlscert;
|
||||
}
|
||||
|
||||
static int _tls_verify(gnutls_session_t session)
|
||||
{
|
||||
tls_t *tls = gnutls_session_get_ptr(session);
|
||||
const gnutls_datum_t *cert_list;
|
||||
gnutls_certificate_type_t type;
|
||||
gnutls_datum_t out;
|
||||
unsigned int cert_list_size = 0, status;
|
||||
gnutls_x509_crt_t cert;
|
||||
|
||||
if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
|
||||
return -1;
|
||||
|
||||
if (gnutls_certificate_verify_peers2(session, &status) < 0) {
|
||||
strophe_error(tls->ctx, "tls", "Verify peers failed");
|
||||
return -1;
|
||||
}
|
||||
type = gnutls_certificate_type_get(session);
|
||||
if (gnutls_certificate_verification_status_print(status, type, &out, 0) <
|
||||
0) {
|
||||
strophe_error(tls->ctx, "tls", "Status print failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return early if the Certificate is trusted
|
||||
* OR if we trust all Certificates */
|
||||
if (status == 0 || tls->conn->tls_trust) {
|
||||
gnutls_free(out.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!tls->conn->certfail_handler) {
|
||||
strophe_error(tls->ctx, "tls",
|
||||
"No certfail handler set, canceling connection attempt");
|
||||
gnutls_free(out.data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
|
||||
|
||||
/* OpenSSL displays the certificate chain in reverse order than GnuTLS.
|
||||
* To show consistent behavior to the user, traverse the list from the
|
||||
* end.
|
||||
*/
|
||||
while (cert_list_size--) {
|
||||
gnutls_x509_crt_init(&cert);
|
||||
|
||||
gnutls_x509_crt_import(cert, &cert_list[cert_list_size],
|
||||
GNUTLS_X509_FMT_DER);
|
||||
|
||||
xmpp_tlscert_t *tlscert = _x509_to_tlscert(tls->ctx, cert);
|
||||
|
||||
if (!tlscert) {
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
gnutls_free(out.data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tls->conn->certfail_handler(tlscert, (char *)out.data) == 0) {
|
||||
xmpp_tlscert_free(tlscert);
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
gnutls_free(out.data);
|
||||
return -1;
|
||||
}
|
||||
xmpp_tlscert_free(tlscert);
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
}
|
||||
gnutls_free(out.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tls_t *tls_new(xmpp_conn_t *conn)
|
||||
{
|
||||
tls_t *tls = xmpp_alloc(conn->ctx, sizeof(tls_t));
|
||||
tls_t *tls = strophe_alloc(conn->ctx, sizeof(tls_t));
|
||||
|
||||
if (tls) {
|
||||
memset(tls, 0, sizeof(*tls));
|
||||
tls->ctx = conn->ctx;
|
||||
tls->sock = conn->sock;
|
||||
tls->conn = conn;
|
||||
gnutls_init(&tls->session, GNUTLS_CLIENT);
|
||||
|
||||
gnutls_certificate_allocate_credentials(&tls->cred);
|
||||
tls_set_credentials(tls, CAFILE);
|
||||
tls_set_credentials(tls, NULL);
|
||||
|
||||
if (conn->password_callback)
|
||||
gnutls_certificate_set_pin_function(tls->cred,
|
||||
_tls_password_callback, conn);
|
||||
|
||||
if (conn->tls_client_cert && conn->tls_client_key) {
|
||||
unsigned int retries = 0;
|
||||
tls->client_cert = _tls_load_cert(conn);
|
||||
if (!tls->client_cert) {
|
||||
strophe_error(tls->ctx, "tls",
|
||||
"could not read client certificate");
|
||||
goto error_out;
|
||||
}
|
||||
while (retries++ < conn->password_retries) {
|
||||
int err = gnutls_certificate_set_x509_key_file(
|
||||
tls->cred, conn->tls_client_cert, conn->tls_client_key,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
if (err == 0)
|
||||
break;
|
||||
tls_clear_password_cache(conn);
|
||||
if (err != GNUTLS_E_DECRYPTION_FAILED) {
|
||||
strophe_error(tls->ctx, "tls",
|
||||
"could not read private key");
|
||||
goto error_out;
|
||||
}
|
||||
strophe_debug(tls->ctx, "tls", "wrong password?");
|
||||
}
|
||||
} else if (conn->tls_client_cert) {
|
||||
unsigned int retries = 0;
|
||||
|
||||
while (retries++ < conn->password_retries) {
|
||||
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
|
||||
pass[0] = '\0';
|
||||
int passlen = _tls_password_callback(conn, 0, NULL, NULL, 0,
|
||||
pass, sizeof(pass));
|
||||
if (passlen < 0)
|
||||
continue;
|
||||
int err = gnutls_certificate_set_x509_simple_pkcs12_file(
|
||||
tls->cred, conn->tls_client_cert, GNUTLS_X509_FMT_DER,
|
||||
pass);
|
||||
memset(pass, 0, sizeof(pass));
|
||||
if (err == 0)
|
||||
break;
|
||||
tls_clear_password_cache(conn);
|
||||
if (err != GNUTLS_E_DECRYPTION_FAILED &&
|
||||
err != GNUTLS_E_MAC_VERIFY_FAILED) {
|
||||
strophe_error(tls->ctx, "tls", "could not read P12 file");
|
||||
goto error_out;
|
||||
}
|
||||
strophe_debug(tls->ctx, "tls", "wrong password?");
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_certificate_set_verify_function(tls->cred, _tls_verify);
|
||||
|
||||
gnutls_set_default_priority(tls->session);
|
||||
gnutls_session_set_ptr(tls->session, tls);
|
||||
|
||||
/* fixme: this may require setting a callback on win32? */
|
||||
gnutls_transport_set_int(tls->session, conn->sock);
|
||||
}
|
||||
|
||||
return tls;
|
||||
error_out:
|
||||
if (tls->client_cert)
|
||||
gnutls_x509_crt_deinit(tls->client_cert);
|
||||
gnutls_certificate_free_credentials(tls->cred);
|
||||
gnutls_deinit(tls->session);
|
||||
strophe_free(tls->ctx, tls);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void tls_free(tls_t *tls)
|
||||
{
|
||||
gnutls_free(tls->channel_binding.data);
|
||||
gnutls_x509_crt_deinit(tls->client_cert);
|
||||
gnutls_deinit(tls->session);
|
||||
gnutls_certificate_free_credentials(tls->cred);
|
||||
xmpp_free(tls->ctx, tls);
|
||||
strophe_free(tls->ctx, tls);
|
||||
}
|
||||
|
||||
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
|
||||
{
|
||||
xmpp_tlscert_t *tlscert = NULL;
|
||||
if (conn && conn->tls && conn->tls->session) {
|
||||
unsigned int list_size = 0;
|
||||
const gnutls_datum_t *der_cert =
|
||||
gnutls_certificate_get_peers(conn->tls->session, &list_size);
|
||||
if (der_cert && list_size) {
|
||||
gnutls_x509_crt_t cert;
|
||||
if (gnutls_x509_crt_init(&cert) < 0)
|
||||
return NULL;
|
||||
if (gnutls_x509_crt_import(cert, der_cert, GNUTLS_X509_FMT_DER) ==
|
||||
0)
|
||||
tlscert = _x509_to_tlscert(conn->ctx, cert);
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
}
|
||||
}
|
||||
return tlscert;
|
||||
}
|
||||
|
||||
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||
{
|
||||
int err;
|
||||
UNUSED(cafilename);
|
||||
|
||||
/* set trusted credentials -- takes a .pem filename */
|
||||
err = gnutls_certificate_set_x509_trust_file(tls->cred, cafilename,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
int err = gnutls_certificate_set_x509_system_trust(tls->cred);
|
||||
if (err >= 0 && tls->conn->tls_cafile)
|
||||
err = gnutls_certificate_set_x509_trust_file(
|
||||
tls->cred, tls->conn->tls_cafile, GNUTLS_X509_FMT_PEM);
|
||||
if (err >= 0 && tls->conn->tls_capath)
|
||||
err = gnutls_certificate_set_x509_trust_dir(
|
||||
tls->cred, tls->conn->tls_capath, GNUTLS_X509_FMT_PEM);
|
||||
if (err >= 0) {
|
||||
err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE,
|
||||
tls->cred);
|
||||
@@ -90,11 +569,60 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||
return err == GNUTLS_E_SUCCESS;
|
||||
}
|
||||
|
||||
int tls_init_channel_binding(tls_t *tls,
|
||||
const char **binding_prefix,
|
||||
size_t *binding_prefix_len)
|
||||
{
|
||||
gnutls_channel_binding_t binding_type;
|
||||
gnutls_protocol_t tls_version = gnutls_protocol_get_version(tls->session);
|
||||
|
||||
switch (tls_version) {
|
||||
case GNUTLS_SSL3:
|
||||
case GNUTLS_TLS1_0:
|
||||
case GNUTLS_TLS1_1:
|
||||
case GNUTLS_TLS1_2:
|
||||
*binding_prefix = "tls-unique";
|
||||
*binding_prefix_len = strlen("tls-unique");
|
||||
binding_type = GNUTLS_CB_TLS_UNIQUE;
|
||||
break;
|
||||
case GNUTLS_TLS1_3:
|
||||
*binding_prefix = "tls-exporter";
|
||||
*binding_prefix_len = strlen("tls-exporter");
|
||||
binding_type = GNUTLS_CB_TLS_EXPORTER;
|
||||
break;
|
||||
default:
|
||||
strophe_error(tls->ctx, "tls", "Unsupported TLS Version: %s",
|
||||
gnutls_protocol_get_name(tls_version));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tls->channel_binding.data) {
|
||||
gnutls_free(tls->channel_binding.data);
|
||||
tls->channel_binding.data = NULL;
|
||||
}
|
||||
int ret = gnutls_session_channel_binding(tls->session, binding_type,
|
||||
&tls->channel_binding);
|
||||
if (ret) {
|
||||
strophe_error(tls->ctx, "tls", "could not get channel binding: %s",
|
||||
gnutls_strerror(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
|
||||
{
|
||||
if (!tls->channel_binding.data || !tls->channel_binding.size) {
|
||||
strophe_error(tls->ctx, "tls", "No channel binding data available");
|
||||
}
|
||||
*size = tls->channel_binding.size;
|
||||
return tls->channel_binding.data;
|
||||
}
|
||||
|
||||
int tls_start(tls_t *tls)
|
||||
{
|
||||
sock_set_blocking(tls->sock);
|
||||
sock_set_blocking(tls->conn->sock);
|
||||
tls->lasterror = gnutls_handshake(tls->session);
|
||||
sock_set_nonblocking(tls->sock);
|
||||
sock_set_nonblocking(tls->conn->sock);
|
||||
|
||||
return tls->lasterror == GNUTLS_E_SUCCESS;
|
||||
}
|
||||
@@ -105,24 +633,26 @@ int tls_stop(tls_t *tls)
|
||||
return tls->lasterror == GNUTLS_E_SUCCESS;
|
||||
}
|
||||
|
||||
int tls_error(tls_t *tls)
|
||||
int tls_error(struct conn_interface *intf)
|
||||
{
|
||||
return tls->lasterror;
|
||||
return intf->conn->tls->lasterror;
|
||||
}
|
||||
|
||||
int tls_is_recoverable(int error)
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||
{
|
||||
UNUSED(intf);
|
||||
return !gnutls_error_is_fatal(error);
|
||||
}
|
||||
|
||||
int tls_pending(tls_t *tls)
|
||||
int tls_pending(struct conn_interface *intf)
|
||||
{
|
||||
return gnutls_record_check_pending(tls->session);
|
||||
return gnutls_record_check_pending(intf->conn->tls->session);
|
||||
}
|
||||
|
||||
int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
int ret;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = gnutls_record_recv(tls->session, buff, len);
|
||||
tls->lasterror = ret < 0 ? ret : 0;
|
||||
@@ -130,9 +660,10 @@ int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tls_write(tls_t *tls, const void *const buff, const size_t len)
|
||||
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
int ret;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = gnutls_record_send(tls->session, buff, len);
|
||||
tls->lasterror = ret < 0 ? ret : 0;
|
||||
@@ -140,8 +671,8 @@ int tls_write(tls_t *tls, const void *const buff, const size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls)
|
||||
int tls_clear_pending_write(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* tls_schannel.c
|
||||
** strophe XMPP client library -- TLS abstraction schannel impl.
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -61,6 +62,21 @@ void tls_shutdown(void)
|
||||
return;
|
||||
}
|
||||
|
||||
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
|
||||
{
|
||||
UNUSED(n);
|
||||
/* always fail */
|
||||
strophe_error(conn->ctx, "tls", "Client-Authentication not implemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
|
||||
{
|
||||
/* always fail */
|
||||
strophe_error(conn->ctx, "tls", "Client-Authentication not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tls_t *tls_new(xmpp_conn_t *conn)
|
||||
{
|
||||
xmpp_ctx_t *ctx = conn->ctx;
|
||||
@@ -87,7 +103,7 @@ tls_t *tls_new(xmpp_conn_t *conn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tls = xmpp_alloc(ctx, sizeof(*tls));
|
||||
tls = strophe_alloc(ctx, sizeof(*tls));
|
||||
|
||||
if (!tls) {
|
||||
return NULL;
|
||||
@@ -123,7 +139,7 @@ tls_t *tls_new(xmpp_conn_t *conn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xmpp_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
|
||||
strophe_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
|
||||
|
||||
memset(&scred, 0, sizeof(scred));
|
||||
scred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||
@@ -148,7 +164,7 @@ tls_t *tls_new(xmpp_conn_t *conn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xmpp_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
|
||||
strophe_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
|
||||
|
||||
tls->init = 1;
|
||||
|
||||
@@ -181,15 +197,15 @@ tls_t *tls_new(xmpp_conn_t *conn)
|
||||
void tls_free(tls_t *tls)
|
||||
{
|
||||
if (tls->recvbuffer) {
|
||||
xmpp_free(tls->ctx, tls->recvbuffer);
|
||||
strophe_free(tls->ctx, tls->recvbuffer);
|
||||
}
|
||||
|
||||
if (tls->readybuffer) {
|
||||
xmpp_free(tls->ctx, tls->readybuffer);
|
||||
strophe_free(tls->ctx, tls->readybuffer);
|
||||
}
|
||||
|
||||
if (tls->sendbuffer) {
|
||||
xmpp_free(tls->ctx, tls->sendbuffer);
|
||||
strophe_free(tls->ctx, tls->sendbuffer);
|
||||
}
|
||||
|
||||
if (tls->init) {
|
||||
@@ -203,10 +219,17 @@ void tls_free(tls_t *tls)
|
||||
tls->hsec32 = NULL;
|
||||
}
|
||||
|
||||
xmpp_free(tls->ctx, tls);
|
||||
strophe_free(tls->ctx, tls);
|
||||
return;
|
||||
}
|
||||
|
||||
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
|
||||
{
|
||||
/* always fail */
|
||||
strophe_error(conn->ctx, "tls", "tls_peer_cert() not implemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||
{
|
||||
UNUSED(tls);
|
||||
@@ -214,6 +237,23 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_init_channel_binding(tls_t *tls,
|
||||
const char **binding_prefix,
|
||||
size_t *binding_prefix_len)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(binding_prefix);
|
||||
UNUSED(binding_prefix_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tls_start(tls_t *tls)
|
||||
{
|
||||
ULONG ctxtreq = 0, ctxtattr = 0;
|
||||
@@ -222,9 +262,11 @@ int tls_start(tls_t *tls)
|
||||
SECURITY_STATUS ret;
|
||||
int sent;
|
||||
char *name;
|
||||
struct conn_interface *intf;
|
||||
|
||||
/* use the domain there as our name */
|
||||
name = tls->conn->domain;
|
||||
intf = tls->conn->intf;
|
||||
|
||||
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
|
||||
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
|
||||
@@ -241,7 +283,7 @@ int tls_start(tls_t *tls)
|
||||
|
||||
memset(&(sbin[0]), 0, sizeof(sbin[0]));
|
||||
sbin[0].BufferType = SECBUFFER_TOKEN;
|
||||
sbin[0].pvBuffer = xmpp_alloc(tls->ctx, tls->spi->cbMaxToken);
|
||||
sbin[0].pvBuffer = strophe_alloc(tls->ctx, tls->spi->cbMaxToken);
|
||||
sbin[0].cbBuffer = tls->spi->cbMaxToken;
|
||||
|
||||
memset(&(sbin[1]), 0, sizeof(sbin[1]));
|
||||
@@ -273,9 +315,9 @@ int tls_start(tls_t *tls)
|
||||
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
||||
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
||||
|
||||
sent = sock_write(tls->sock, writebuff, writelen);
|
||||
sent = sock_write(intf, writebuff, writelen);
|
||||
if (sent == -1) {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
} else {
|
||||
writebuff += sent;
|
||||
writelen -= sent;
|
||||
@@ -313,13 +355,13 @@ int tls_start(tls_t *tls)
|
||||
|
||||
select(tls->sock, &fds, NULL, NULL, &tv);
|
||||
|
||||
inbytes = sock_read(tls->sock, p, tls->spi->cbMaxToken - len);
|
||||
inbytes = sock_read(intf, p, tls->spi->cbMaxToken - len);
|
||||
|
||||
if (inbytes > 0) {
|
||||
len += inbytes;
|
||||
p += inbytes;
|
||||
} else {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,9 +376,9 @@ int tls_start(tls_t *tls)
|
||||
if (sbdout.pBuffers[0].cbBuffer) {
|
||||
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
||||
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
||||
sent = sock_write(tls->sock, writebuff, writelen);
|
||||
sent = sock_write(intf, writebuff, writelen);
|
||||
if (sent == -1) {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
} else {
|
||||
writebuff += sent;
|
||||
writelen -= sent;
|
||||
@@ -347,12 +389,12 @@ int tls_start(tls_t *tls)
|
||||
}
|
||||
}
|
||||
|
||||
xmpp_free(tls->ctx, sbin[0].pvBuffer);
|
||||
strophe_free(tls->ctx, sbin[0].pvBuffer);
|
||||
|
||||
if (ret != SEC_E_OK) {
|
||||
tls->lasterror = ret;
|
||||
xmpp_error(tls->ctx, "TLSS", "Schannel error 0x%lx",
|
||||
(unsigned long)ret);
|
||||
strophe_error(tls->ctx, "TLSS", "Schannel error 0x%lx",
|
||||
(unsigned long)ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -361,16 +403,16 @@ int tls_start(tls_t *tls)
|
||||
|
||||
tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
|
||||
tls->spcss.cbTrailer;
|
||||
tls->recvbuffer = xmpp_alloc(tls->ctx, tls->recvbuffermaxlen);
|
||||
tls->recvbuffer = strophe_alloc(tls->ctx, tls->recvbuffermaxlen);
|
||||
tls->recvbufferpos = 0;
|
||||
|
||||
tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
|
||||
tls->spcss.cbTrailer;
|
||||
tls->sendbuffer = xmpp_alloc(tls->ctx, tls->sendbuffermaxlen);
|
||||
tls->sendbuffer = strophe_alloc(tls->ctx, tls->sendbuffermaxlen);
|
||||
tls->sendbufferpos = 0;
|
||||
tls->sendbufferlen = 0;
|
||||
|
||||
tls->readybuffer = xmpp_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
|
||||
tls->readybuffer = strophe_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
|
||||
tls->readybufferpos = 0;
|
||||
tls->readybufferlen = 0;
|
||||
|
||||
@@ -383,20 +425,22 @@ int tls_stop(tls_t *tls)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_error(tls_t *tls)
|
||||
int tls_error(struct conn_interface *intf)
|
||||
{
|
||||
return tls->lasterror;
|
||||
return intf->conn->tls->lasterror;
|
||||
}
|
||||
|
||||
int tls_is_recoverable(int error)
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||
{
|
||||
UNUSED(intf);
|
||||
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE ||
|
||||
error == WSAEWOULDBLOCK || error == WSAEMSGSIZE ||
|
||||
error == WSAEINPROGRESS);
|
||||
}
|
||||
|
||||
int tls_pending(tls_t *tls)
|
||||
int tls_pending(struct conn_interface *intf)
|
||||
{
|
||||
tls_t *tls = intf->conn->tls;
|
||||
// There are 3 cases:
|
||||
// - there is data in ready buffer, so it is by default pending
|
||||
// - there is data in recv buffer. If it is not decrypted yet, means it
|
||||
@@ -412,9 +456,10 @@ int tls_pending(tls_t *tls)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
int bytes;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
/* first, if we've got some ready data, put that in the buffer */
|
||||
if (tls->readybufferpos < tls->readybufferlen) {
|
||||
@@ -437,7 +482,7 @@ int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
read = tls_read(tls, newbuff, len - bytes);
|
||||
|
||||
if (read == -1) {
|
||||
if (tls_is_recoverable(tls->lasterror)) {
|
||||
if (tls_is_recoverable(intf, tls->lasterror)) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -449,7 +494,7 @@ int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
}
|
||||
|
||||
/* next, top up our recv buffer */
|
||||
bytes = sock_read(tls->sock, tls->recvbuffer + tls->recvbufferpos,
|
||||
bytes = sock_read(intf, tls->recvbuffer + tls->recvbufferpos,
|
||||
tls->recvbuffermaxlen - tls->recvbufferpos);
|
||||
|
||||
if (bytes == 0) {
|
||||
@@ -458,8 +503,8 @@ int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
}
|
||||
|
||||
if (bytes == -1) {
|
||||
if (!tls_is_recoverable(sock_error())) {
|
||||
tls->lasterror = sock_error();
|
||||
if (!tls_is_recoverable(intf, sock_error(intf))) {
|
||||
tls->lasterror = sock_error(intf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -534,16 +579,17 @@ int tls_read(tls_t *tls, void *const buff, const size_t len)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls)
|
||||
int tls_clear_pending_write(struct conn_interface *intf)
|
||||
{
|
||||
tls_t *tls = intf->conn->tls;
|
||||
if (tls->sendbufferpos < tls->sendbufferlen) {
|
||||
int bytes;
|
||||
|
||||
bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos,
|
||||
bytes = sock_write(intf, tls->sendbuffer + tls->sendbufferpos,
|
||||
tls->sendbufferlen - tls->sendbufferpos);
|
||||
|
||||
if (bytes == -1) {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
return -1;
|
||||
} else if (bytes > 0) {
|
||||
tls->sendbufferpos += bytes;
|
||||
@@ -557,12 +603,13 @@ int tls_clear_pending_write(tls_t *tls)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tls_write(tls_t *tls, const void *const buff, const size_t len)
|
||||
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
SecBufferDesc sbdenc;
|
||||
SecBuffer sbenc[4];
|
||||
const unsigned char *p = buff;
|
||||
int sent = 0, ret, remain = len;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = tls_clear_pending_write(tls);
|
||||
if (ret <= 0) {
|
||||
@@ -622,7 +669,7 @@ int tls_write(tls_t *tls, const void *const buff, const size_t len)
|
||||
|
||||
ret = tls_clear_pending_write(tls);
|
||||
|
||||
if (ret == -1 && !tls_is_recoverable(tls_error(tls))) {
|
||||
if (ret == -1 && !tls_is_recoverable(intf, tls_error(tls))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -634,7 +681,8 @@ int tls_write(tls_t *tls, const void *const buff, const size_t len)
|
||||
remain = 0;
|
||||
}
|
||||
|
||||
if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) {
|
||||
if (ret == 0 ||
|
||||
(ret == -1 && tls_is_recoverable(intf, tls_error(tls)))) {
|
||||
return sent;
|
||||
}
|
||||
}
|
||||
|
||||
62
src/util.c
62
src/util.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* util.c
|
||||
** strophe XMPP client library -- various utility functions
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -15,6 +16,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
@@ -36,21 +38,41 @@
|
||||
* @param ctx a Strophe context object
|
||||
* @param s a string
|
||||
*
|
||||
* @return a new allocates string with the same data as s or NULL on error
|
||||
* @return a newly allocated string with the same data as s or NULL on error
|
||||
*/
|
||||
char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s)
|
||||
char *strophe_strdup(const xmpp_ctx_t *ctx, const char *s)
|
||||
{
|
||||
size_t len;
|
||||
char *copy;
|
||||
return strophe_strndup(ctx, s, SIZE_MAX);
|
||||
}
|
||||
|
||||
len = strlen(s);
|
||||
copy = xmpp_alloc(ctx, len + 1);
|
||||
/** Duplicate a string with a maximum length.
|
||||
* This function replaces the standard strndup library call with a version
|
||||
* that uses the Strophe context object's allocator.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
* @param s a string
|
||||
* @param len the maximum length of the string to copy
|
||||
*
|
||||
* @return a newly allocated string that contains at most `len` symbols
|
||||
* of the original string or NULL on error
|
||||
*/
|
||||
char *strophe_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
|
||||
{
|
||||
char *copy;
|
||||
size_t l;
|
||||
|
||||
l = strlen(s);
|
||||
if (l > len)
|
||||
l = len;
|
||||
|
||||
copy = strophe_alloc(ctx, l + 1);
|
||||
if (!copy) {
|
||||
xmpp_error(ctx, "xmpp", "failed to allocate required memory");
|
||||
strophe_error(ctx, "xmpp", "failed to allocate required memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(copy, s, len + 1);
|
||||
memcpy(copy, s, l);
|
||||
copy[l] = '\0';
|
||||
|
||||
return copy;
|
||||
}
|
||||
@@ -60,7 +82,7 @@ char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s)
|
||||
* For example, visual studio older than 2005 doesn't provide strtok_r()
|
||||
* nor strtok_s().
|
||||
*/
|
||||
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
|
||||
char *strophe_strtok_r(char *s, const char *delim, char **saveptr)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
@@ -135,8 +157,24 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
*/
|
||||
void disconnect_mem_error(xmpp_conn_t *const conn)
|
||||
void disconnect_mem_error(xmpp_conn_t *conn)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Memory allocation error");
|
||||
strophe_error(conn->ctx, "xmpp", "Memory allocation error");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
|
||||
int string_to_ul(const char *s, unsigned long *ul)
|
||||
{
|
||||
char *endptr;
|
||||
*ul = strtoul(s, &endptr, 10);
|
||||
return *endptr != '\0';
|
||||
}
|
||||
|
||||
void hex_encode(char *writebuf, void *readbuf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < len; i++) {
|
||||
sprintf(writebuf, "%02x", ((unsigned char *)readbuf)[i]);
|
||||
writebuf += 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* util.h
|
||||
** strophe XMPP client library -- various utility functions
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -26,10 +27,14 @@
|
||||
#define xmpp_min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
/* string functions */
|
||||
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr);
|
||||
char *strophe_strtok_r(char *s, const char *delim, char **saveptr);
|
||||
|
||||
/* timing functions */
|
||||
uint64_t time_stamp(void);
|
||||
uint64_t time_elapsed(uint64_t t1, uint64_t t2);
|
||||
|
||||
/* misc functions */
|
||||
int string_to_ul(const char *s, unsigned long *ul);
|
||||
void hex_encode(char *writebuf, void *readbuf, size_t len);
|
||||
|
||||
#endif /* __LIBSTROPHE_UTIL_H__ */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* uuid.c
|
||||
* strophe XMPP client library -- UUID generation
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -15,7 +16,6 @@
|
||||
|
||||
#include "strophe.h"
|
||||
#include "common.h"
|
||||
#include "rand.h"
|
||||
|
||||
/** @def XMPP_UUID_LEN
|
||||
* UUID length in string representation excluding '\0'.
|
||||
@@ -65,7 +65,7 @@ char *xmpp_uuid_gen(xmpp_ctx_t *ctx)
|
||||
{
|
||||
char *uuid;
|
||||
|
||||
uuid = xmpp_alloc(ctx, XMPP_UUID_LEN + 1);
|
||||
uuid = strophe_alloc(ctx, XMPP_UUID_LEN + 1);
|
||||
if (uuid != NULL) {
|
||||
crypto_uuid_gen(ctx, uuid);
|
||||
}
|
||||
|
||||
607
strophe.h
607
strophe.h
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* strophe.h
|
||||
** strophe XMPP client library C API
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This software is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This software is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@@ -17,6 +18,7 @@
|
||||
#define __LIBSTROPHE_STROPHE_H__
|
||||
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -79,6 +81,18 @@ extern "C" {
|
||||
* Namespace definition for 'jabber:iq:register'.
|
||||
*/
|
||||
#define XMPP_NS_REGISTER "jabber:iq:register"
|
||||
/** @def XMPP_NS_SM
|
||||
* Namespace definition for Stream Management.
|
||||
*/
|
||||
#define XMPP_NS_SM "urn:xmpp:sm:3"
|
||||
/** @def XMPP_NS_COMPRESSION
|
||||
* Namespace definition for Stream Compression.
|
||||
*/
|
||||
#define XMPP_NS_COMPRESSION "http://jabber.org/protocol/compress"
|
||||
/** @def XMPP_NS_FEATURE_COMPRESSION
|
||||
* Namespace definition for Stream Compression.
|
||||
*/
|
||||
#define XMPP_NS_FEATURE_COMPRESSION "http://jabber.org/features/compress"
|
||||
|
||||
/* error defines */
|
||||
/** @def XMPP_EOK
|
||||
@@ -122,18 +136,22 @@ typedef struct _xmpp_log_t xmpp_log_t;
|
||||
/* opaque run time context containing the above hooks */
|
||||
typedef struct _xmpp_ctx_t xmpp_ctx_t;
|
||||
|
||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
|
||||
const xmpp_log_t *const log);
|
||||
void xmpp_ctx_free(xmpp_ctx_t *const ctx);
|
||||
typedef struct _xmpp_tlscert_t xmpp_tlscert_t;
|
||||
|
||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log);
|
||||
void xmpp_ctx_free(xmpp_ctx_t *ctx);
|
||||
|
||||
/* set the verbosity level of the ctx */
|
||||
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level);
|
||||
|
||||
/* free some blocks returned by other APIs, for example the
|
||||
buffer you get from xmpp_stanza_to_text */
|
||||
void xmpp_free(const xmpp_ctx_t *const ctx, void *p);
|
||||
void xmpp_free(const xmpp_ctx_t *ctx, void *p);
|
||||
|
||||
struct _xmpp_mem_t {
|
||||
void *(*alloc)(const size_t size, void *const userdata);
|
||||
void (*free)(void *p, void *const userdata);
|
||||
void *(*realloc)(void *p, const size_t size, void *const userdata);
|
||||
void *(*alloc)(size_t size, void *userdata);
|
||||
void (*free)(void *p, void *userdata);
|
||||
void *(*realloc)(void *p, size_t size, void *userdata);
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
@@ -146,10 +164,10 @@ typedef enum {
|
||||
|
||||
typedef enum { XMPP_UNKNOWN, XMPP_CLIENT, XMPP_COMPONENT } xmpp_conn_type_t;
|
||||
|
||||
typedef void (*xmpp_log_handler)(void *const userdata,
|
||||
const xmpp_log_level_t level,
|
||||
const char *const area,
|
||||
const char *const msg);
|
||||
typedef void (*xmpp_log_handler)(void *userdata,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *msg);
|
||||
|
||||
struct _xmpp_log_t {
|
||||
xmpp_log_handler handler;
|
||||
@@ -164,6 +182,7 @@ xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level);
|
||||
/* opaque connection object */
|
||||
typedef struct _xmpp_conn_t xmpp_conn_t;
|
||||
typedef struct _xmpp_stanza_t xmpp_stanza_t;
|
||||
typedef struct _xmpp_sm_t xmpp_sm_state_t;
|
||||
|
||||
/* connection flags */
|
||||
#define XMPP_CONN_FLAG_DISABLE_TLS (1UL << 0)
|
||||
@@ -177,6 +196,19 @@ typedef struct _xmpp_stanza_t xmpp_stanza_t;
|
||||
* Enable legacy authentication support.
|
||||
*/
|
||||
#define XMPP_CONN_FLAG_LEGACY_AUTH (1UL << 4)
|
||||
/** @def XMPP_CONN_FLAG_DISABLE_SM
|
||||
* Disable Stream-Management XEP-0198.
|
||||
*/
|
||||
#define XMPP_CONN_FLAG_DISABLE_SM (1UL << 5)
|
||||
/** @def XMPP_CONN_FLAG_ENABLE_COMPRESSION
|
||||
* Enable Stream-Compression XEP-0138.
|
||||
*/
|
||||
#define XMPP_CONN_FLAG_ENABLE_COMPRESSION (1UL << 6)
|
||||
/** @def XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
|
||||
* Don't reset the compressed stream after each stanza.
|
||||
* Only enable this flag if you know what you're doing.
|
||||
*/
|
||||
#define XMPP_CONN_FLAG_COMPRESSION_DONT_RESET (1UL << 7)
|
||||
|
||||
/* connect callback */
|
||||
typedef enum {
|
||||
@@ -213,119 +245,282 @@ typedef enum {
|
||||
XMPP_SE_XML_NOT_WELL_FORMED
|
||||
} xmpp_error_type_t;
|
||||
|
||||
/** Certificate Elements
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
typedef enum {
|
||||
XMPP_CERT_VERSION, /**< X.509 Version */
|
||||
XMPP_CERT_SERIALNUMBER, /**< SerialNumber */
|
||||
XMPP_CERT_SUBJECT, /**< Subject */
|
||||
XMPP_CERT_ISSUER, /**< Issuer */
|
||||
XMPP_CERT_NOTBEFORE, /**< Issued on */
|
||||
XMPP_CERT_NOTAFTER, /**< Expires on */
|
||||
XMPP_CERT_KEYALG, /**< Public Key Algorithm */
|
||||
XMPP_CERT_SIGALG, /**< Certificate Signature Algorithm */
|
||||
XMPP_CERT_FINGERPRINT_SHA1, /**< Fingerprint SHA-1 */
|
||||
XMPP_CERT_FINGERPRINT_SHA256, /**< Fingerprint SHA-256 */
|
||||
XMPP_CERT_ELEMENT_MAX /**< Last element of the enum */
|
||||
} xmpp_cert_element_t;
|
||||
|
||||
typedef struct {
|
||||
xmpp_error_type_t type;
|
||||
char *text;
|
||||
xmpp_stanza_t *stanza;
|
||||
} xmpp_stream_error_t;
|
||||
|
||||
typedef void (*xmpp_conn_handler)(xmpp_conn_t *const conn,
|
||||
const xmpp_conn_event_t event,
|
||||
const int error,
|
||||
xmpp_stream_error_t *const stream_error,
|
||||
void *const userdata);
|
||||
typedef void (*xmpp_conn_handler)(xmpp_conn_t *conn,
|
||||
xmpp_conn_event_t event,
|
||||
int error,
|
||||
xmpp_stream_error_t *stream_error,
|
||||
void *userdata);
|
||||
|
||||
void xmpp_send_error(xmpp_conn_t *const conn,
|
||||
xmpp_error_type_t const type,
|
||||
char *const text);
|
||||
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *const ctx);
|
||||
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *const conn);
|
||||
int xmpp_conn_release(xmpp_conn_t *const conn);
|
||||
/** The Handler function which will be called when the TLS stack can't
|
||||
* verify the authenticity of a Certificate that gets presented by
|
||||
* the server we're trying to connect to.
|
||||
*
|
||||
* When this function is called and details of the `cert` have to be
|
||||
* kept, please copy them yourself. The `cert` object will be free'd
|
||||
* automatically when this function returns.
|
||||
*
|
||||
* NB: `errormsg` is specific per certificate on OpenSSL and the same
|
||||
* for all certificates on GnuTLS.
|
||||
*
|
||||
* @param cert a Strophe certificate object
|
||||
* @param errormsg The error that caused this.
|
||||
*
|
||||
* @return 0 if the connection attempt should be terminated,
|
||||
* 1 if the connection should be established.
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
typedef int (*xmpp_certfail_handler)(const xmpp_tlscert_t *cert,
|
||||
const char *const errormsg);
|
||||
|
||||
long xmpp_conn_get_flags(const xmpp_conn_t *const conn);
|
||||
int xmpp_conn_set_flags(xmpp_conn_t *const conn, long flags);
|
||||
const char *xmpp_conn_get_jid(const xmpp_conn_t *const conn);
|
||||
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *const conn);
|
||||
void xmpp_conn_set_jid(xmpp_conn_t *const conn, const char *const jid);
|
||||
const char *xmpp_conn_get_pass(const xmpp_conn_t *const conn);
|
||||
void xmpp_conn_set_pass(xmpp_conn_t *const conn, const char *const pass);
|
||||
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *const conn);
|
||||
void xmpp_conn_disable_tls(xmpp_conn_t *const conn);
|
||||
int xmpp_conn_is_secured(xmpp_conn_t *const conn);
|
||||
void xmpp_conn_set_keepalive(xmpp_conn_t *const conn,
|
||||
int timeout,
|
||||
int interval);
|
||||
int xmpp_conn_is_connecting(xmpp_conn_t *const conn);
|
||||
int xmpp_conn_is_connected(xmpp_conn_t *const conn);
|
||||
int xmpp_conn_is_disconnected(xmpp_conn_t *const conn);
|
||||
/** The Handler function which will be called when the TLS stack can't
|
||||
* decrypt a password protected key file.
|
||||
*
|
||||
* When this callback is called it shall write a NULL-terminated
|
||||
* string of maximum length `pw_max - 1` to `pw`.
|
||||
*
|
||||
* This is currently only supported for GnuTLS and OpenSSL.
|
||||
*
|
||||
* On 2022-02-02 the following maximum lengths are valid:
|
||||
* ```
|
||||
* include/gnutls/pkcs11.h: #define GNUTLS_PKCS11_MAX_PIN_LEN 256
|
||||
* include/openssl/pem.h: #define PEM_BUFSIZE 1024
|
||||
* ```
|
||||
*
|
||||
* We expect the buffer to be NULL-terminated, therefore the usable lengths
|
||||
* are:
|
||||
*
|
||||
* * 255 for GnuTLS
|
||||
* * 1023 for OpenSSL
|
||||
*
|
||||
* Useful API's inside this callback are e.g.
|
||||
*
|
||||
* \ref xmpp_conn_get_keyfile
|
||||
*
|
||||
*
|
||||
* @param pw The buffer where the password shall be stored.
|
||||
* @param pw_max The maximum length of the password.
|
||||
* @param conn The Strophe connection object this callback originates from.
|
||||
* @param userdata The userdata pointer as supplied when setting this callback.
|
||||
*
|
||||
* @return -1 on error, else the number of bytes written to `pw` w/o
|
||||
* terminating NUL byte
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
typedef int (*xmpp_password_callback)(char *pw,
|
||||
size_t pw_max,
|
||||
xmpp_conn_t *conn,
|
||||
void *userdata);
|
||||
|
||||
int xmpp_connect_client(xmpp_conn_t *const conn,
|
||||
const char *const altdomain,
|
||||
/** The function which will be called when Strophe creates a new socket.
|
||||
*
|
||||
* The `sock` argument is a pointer that is dependent on the architecture
|
||||
* Strophe is compiled for.
|
||||
*
|
||||
* For POSIX compatible systems usage shall be:
|
||||
* ```
|
||||
* int soc = *((int*)sock);
|
||||
* ```
|
||||
*
|
||||
* On Windows usage shall be:
|
||||
* ```
|
||||
* SOCKET soc = *((SOCKET*)sock);
|
||||
* ```
|
||||
*
|
||||
* This function will be called for each socket that is created.
|
||||
*
|
||||
* `examples/bot.c` uses a libstrophe supplied callback function that sets
|
||||
* basic keepalive parameters (`xmpp_sockopt_cb_keepalive()`).
|
||||
*
|
||||
* `examples/complex.c` implements a custom function that could be useful
|
||||
* for an application.
|
||||
*
|
||||
* @param conn The Strophe connection object this callback originates from.
|
||||
* @param sock A pointer to the underlying file descriptor.
|
||||
*
|
||||
* @return 0 on success, -1 on error
|
||||
*
|
||||
* @ingroup Connections
|
||||
*/
|
||||
typedef int (*xmpp_sockopt_callback)(xmpp_conn_t *conn, void *sock);
|
||||
|
||||
/* an example callback that sets basic keepalive parameters */
|
||||
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *sock);
|
||||
|
||||
void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text);
|
||||
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx);
|
||||
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn);
|
||||
int xmpp_conn_release(xmpp_conn_t *conn);
|
||||
|
||||
long xmpp_conn_get_flags(const xmpp_conn_t *conn);
|
||||
int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags);
|
||||
const char *xmpp_conn_get_jid(const xmpp_conn_t *conn);
|
||||
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn);
|
||||
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid);
|
||||
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path);
|
||||
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path);
|
||||
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
|
||||
xmpp_certfail_handler hndl);
|
||||
xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn);
|
||||
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
|
||||
xmpp_password_callback cb,
|
||||
void *userdata);
|
||||
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries);
|
||||
const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn);
|
||||
void xmpp_conn_set_client_cert(xmpp_conn_t *conn,
|
||||
const char *cert,
|
||||
const char *key);
|
||||
unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *conn);
|
||||
char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *conn, unsigned int n);
|
||||
const char *xmpp_conn_get_pass(const xmpp_conn_t *conn);
|
||||
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass);
|
||||
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn);
|
||||
int xmpp_conn_is_secured(xmpp_conn_t *conn);
|
||||
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
|
||||
xmpp_sockopt_callback callback);
|
||||
int xmpp_conn_is_connecting(xmpp_conn_t *conn);
|
||||
int xmpp_conn_is_connected(xmpp_conn_t *conn);
|
||||
int xmpp_conn_is_disconnected(xmpp_conn_t *conn);
|
||||
int xmpp_conn_send_queue_len(const xmpp_conn_t *conn);
|
||||
|
||||
typedef enum {
|
||||
XMPP_QUEUE_OLDEST = -1,
|
||||
XMPP_QUEUE_YOUNGEST = -2,
|
||||
} xmpp_queue_element_t;
|
||||
char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
|
||||
xmpp_queue_element_t which);
|
||||
|
||||
int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state);
|
||||
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn);
|
||||
|
||||
/** The function which will be called when Strophe updates its internal SM
|
||||
* state.
|
||||
*
|
||||
* Please note that you have to create a copy of the buffer, since the library
|
||||
* will free the buffer right after return of the callback function.
|
||||
*
|
||||
*
|
||||
* @param conn The Strophe connection object this callback
|
||||
* originates from.
|
||||
* @param ctx The `ctx` pointer as passed to
|
||||
* \ref xmpp_conn_set_sm_callback
|
||||
* @param sm_state A pointer to a buffer containing the serialized SM
|
||||
* state.
|
||||
* @param sm_state_len The length of `sm_state`.
|
||||
*
|
||||
* @return 0 on success, -1 on error
|
||||
*
|
||||
* @ingroup Connections
|
||||
*/
|
||||
typedef void (*xmpp_sm_callback)(xmpp_conn_t *conn,
|
||||
void *ctx,
|
||||
const unsigned char *sm_state,
|
||||
size_t sm_state_len);
|
||||
void xmpp_conn_set_sm_callback(xmpp_conn_t *conn,
|
||||
xmpp_sm_callback cb,
|
||||
void *ctx);
|
||||
int xmpp_conn_restore_sm_state(xmpp_conn_t *conn,
|
||||
const unsigned char *sm_state,
|
||||
size_t sm_state_len);
|
||||
|
||||
void xmpp_free_sm_state(xmpp_sm_state_t *sm_state);
|
||||
|
||||
int xmpp_connect_client(xmpp_conn_t *conn,
|
||||
const char *altdomain,
|
||||
unsigned short altport,
|
||||
xmpp_conn_handler callback,
|
||||
void *const userdata);
|
||||
void *userdata);
|
||||
|
||||
int xmpp_connect_component(xmpp_conn_t *const conn,
|
||||
const char *const server,
|
||||
int xmpp_connect_component(xmpp_conn_t *conn,
|
||||
const char *server,
|
||||
unsigned short port,
|
||||
xmpp_conn_handler callback,
|
||||
void *const userdata);
|
||||
void *userdata);
|
||||
|
||||
int xmpp_connect_raw(xmpp_conn_t *const conn,
|
||||
const char *const altdomain,
|
||||
int xmpp_connect_raw(xmpp_conn_t *conn,
|
||||
const char *altdomain,
|
||||
unsigned short altport,
|
||||
xmpp_conn_handler callback,
|
||||
void *const userdata);
|
||||
int xmpp_conn_open_stream_default(xmpp_conn_t *const conn);
|
||||
int xmpp_conn_open_stream(xmpp_conn_t *const conn,
|
||||
void *userdata);
|
||||
int xmpp_conn_open_stream_default(xmpp_conn_t *conn);
|
||||
int xmpp_conn_open_stream(xmpp_conn_t *conn,
|
||||
char **attributes,
|
||||
size_t attributes_len);
|
||||
int xmpp_conn_tls_start(xmpp_conn_t *const conn);
|
||||
int xmpp_conn_tls_cafile(xmpp_conn_t *const conn, const char *cafile);
|
||||
int xmpp_conn_tls_start(xmpp_conn_t *conn);
|
||||
|
||||
void xmpp_disconnect(xmpp_conn_t *const conn);
|
||||
void xmpp_disconnect(xmpp_conn_t *conn);
|
||||
|
||||
void xmpp_send(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza);
|
||||
void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
|
||||
|
||||
void xmpp_send_raw_string(xmpp_conn_t *const conn, const char *const fmt, ...);
|
||||
void xmpp_send_raw(xmpp_conn_t *const conn,
|
||||
const char *const data,
|
||||
const size_t len);
|
||||
void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
|
||||
void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len);
|
||||
|
||||
/* handlers */
|
||||
|
||||
/* if the handler returns false it is removed */
|
||||
typedef int (*xmpp_timed_handler)(xmpp_conn_t *const conn,
|
||||
void *const userdata);
|
||||
typedef int (*xmpp_timed_handler)(xmpp_conn_t *conn, void *userdata);
|
||||
|
||||
void xmpp_timed_handler_add(xmpp_conn_t *const conn,
|
||||
void xmpp_timed_handler_add(xmpp_conn_t *conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void *const userdata);
|
||||
void xmpp_timed_handler_delete(xmpp_conn_t *const conn,
|
||||
xmpp_timed_handler handler);
|
||||
unsigned long period,
|
||||
void *userdata);
|
||||
void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler);
|
||||
|
||||
/* if the handler returns false it is removed */
|
||||
typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *const ctx,
|
||||
void *const userdata);
|
||||
typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *ctx, void *userdata);
|
||||
|
||||
void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx,
|
||||
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
|
||||
xmpp_global_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void *const userdata);
|
||||
void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx,
|
||||
unsigned long period,
|
||||
void *userdata);
|
||||
void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
|
||||
xmpp_global_timed_handler handler);
|
||||
|
||||
/* if the handler returns false it is removed */
|
||||
typedef int (*xmpp_handler)(xmpp_conn_t *const conn,
|
||||
xmpp_stanza_t *const stanza,
|
||||
void *const userdata);
|
||||
typedef int (*xmpp_handler)(xmpp_conn_t *conn,
|
||||
xmpp_stanza_t *stanza,
|
||||
void *userdata);
|
||||
|
||||
void xmpp_handler_add(xmpp_conn_t *const conn,
|
||||
void xmpp_handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const ns,
|
||||
const char *const name,
|
||||
const char *const type,
|
||||
void *const userdata);
|
||||
void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler);
|
||||
const char *ns,
|
||||
const char *name,
|
||||
const char *type,
|
||||
void *userdata);
|
||||
void xmpp_handler_delete(xmpp_conn_t *conn, xmpp_handler handler);
|
||||
|
||||
void xmpp_id_handler_add(xmpp_conn_t *const conn,
|
||||
void xmpp_id_handler_add(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const id,
|
||||
void *const userdata);
|
||||
void xmpp_id_handler_delete(xmpp_conn_t *const conn,
|
||||
const char *id,
|
||||
void *userdata);
|
||||
void xmpp_id_handler_delete(xmpp_conn_t *conn,
|
||||
xmpp_handler handler,
|
||||
const char *const id);
|
||||
const char *id);
|
||||
|
||||
/*
|
||||
void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
|
||||
@@ -338,94 +533,91 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx);
|
||||
xmpp_stanza_t *xmpp_stanza_new_from_string(xmpp_ctx_t *ctx, const char *str);
|
||||
|
||||
/* clone a stanza */
|
||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *stanza);
|
||||
|
||||
/* copies a stanza and all children */
|
||||
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *const stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *stanza);
|
||||
|
||||
/* free a stanza object and it's contents */
|
||||
int xmpp_stanza_release(xmpp_stanza_t *const stanza);
|
||||
int xmpp_stanza_release(xmpp_stanza_t *stanza);
|
||||
|
||||
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza);
|
||||
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *stanza);
|
||||
|
||||
int xmpp_stanza_is_text(xmpp_stanza_t *const stanza);
|
||||
int xmpp_stanza_is_tag(xmpp_stanza_t *const stanza);
|
||||
int xmpp_stanza_is_text(xmpp_stanza_t *stanza);
|
||||
int xmpp_stanza_is_tag(xmpp_stanza_t *stanza);
|
||||
|
||||
/* marshall a stanza into text for transmission or display */
|
||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
char **const buf,
|
||||
size_t *const buflen);
|
||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza, char **buf, size_t *buflen);
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *const stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *const stanza,
|
||||
const char *const name);
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza,
|
||||
const char *const ns);
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
|
||||
const char *const name,
|
||||
const char *const ns);
|
||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *stanza,
|
||||
const char *name);
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *stanza,
|
||||
const char *ns);
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *stanza,
|
||||
const char *name,
|
||||
const char *ns);
|
||||
/* helper macro for names with a namespace */
|
||||
#define XMPP_STANZA_NAME_IN_NS(name, ns) name "[@ns='" ns "']"
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_path(xmpp_stanza_t *stanza, ...);
|
||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *stanza);
|
||||
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
|
||||
int xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza,
|
||||
xmpp_stanza_t *child,
|
||||
int do_clone);
|
||||
|
||||
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza,
|
||||
const char *const name);
|
||||
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza);
|
||||
int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
|
||||
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *stanza, const char *name);
|
||||
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *stanza);
|
||||
int xmpp_stanza_get_attributes(xmpp_stanza_t *stanza,
|
||||
const char **attr,
|
||||
int attrlen);
|
||||
/* concatenate all child text nodes. this function
|
||||
* returns a string that must be freed by the caller */
|
||||
char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza);
|
||||
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *const stanza);
|
||||
const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza);
|
||||
char *xmpp_stanza_get_text(xmpp_stanza_t *stanza);
|
||||
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *stanza);
|
||||
const char *xmpp_stanza_get_name(xmpp_stanza_t *stanza);
|
||||
/* set_attribute adds/replaces attributes */
|
||||
int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
|
||||
const char *const key,
|
||||
const char *const value);
|
||||
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name);
|
||||
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text);
|
||||
int xmpp_stanza_set_attribute(xmpp_stanza_t *stanza,
|
||||
const char *key,
|
||||
const char *value);
|
||||
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *name);
|
||||
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *text);
|
||||
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||
const char *const text,
|
||||
const size_t size);
|
||||
int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
|
||||
const char *const name);
|
||||
const char *text,
|
||||
size_t size);
|
||||
int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name);
|
||||
|
||||
/* common stanza helpers */
|
||||
const char *xmpp_stanza_get_ns(xmpp_stanza_t *const stanza);
|
||||
const char *xmpp_stanza_get_type(xmpp_stanza_t *const stanza);
|
||||
const char *xmpp_stanza_get_id(xmpp_stanza_t *const stanza);
|
||||
const char *xmpp_stanza_get_to(xmpp_stanza_t *const stanza);
|
||||
const char *xmpp_stanza_get_from(xmpp_stanza_t *const stanza);
|
||||
int xmpp_stanza_set_ns(xmpp_stanza_t *const stanza, const char *const ns);
|
||||
int xmpp_stanza_set_id(xmpp_stanza_t *const stanza, const char *const id);
|
||||
int xmpp_stanza_set_type(xmpp_stanza_t *const stanza, const char *const type);
|
||||
int xmpp_stanza_set_to(xmpp_stanza_t *const stanza, const char *const to);
|
||||
int xmpp_stanza_set_from(xmpp_stanza_t *const stanza, const char *const from);
|
||||
const char *xmpp_stanza_get_ns(xmpp_stanza_t *stanza);
|
||||
const char *xmpp_stanza_get_type(xmpp_stanza_t *stanza);
|
||||
const char *xmpp_stanza_get_id(xmpp_stanza_t *stanza);
|
||||
const char *xmpp_stanza_get_to(xmpp_stanza_t *stanza);
|
||||
const char *xmpp_stanza_get_from(xmpp_stanza_t *stanza);
|
||||
int xmpp_stanza_set_ns(xmpp_stanza_t *stanza, const char *ns);
|
||||
int xmpp_stanza_set_id(xmpp_stanza_t *stanza, const char *id);
|
||||
int xmpp_stanza_set_type(xmpp_stanza_t *stanza, const char *type);
|
||||
int xmpp_stanza_set_to(xmpp_stanza_t *stanza, const char *to);
|
||||
int xmpp_stanza_set_from(xmpp_stanza_t *stanza, const char *from);
|
||||
|
||||
/* allocate and initialize a stanza in reply to another */
|
||||
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
|
||||
const char *const error_type,
|
||||
const char *const condition,
|
||||
const char *const text);
|
||||
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *stanza,
|
||||
const char *error_type,
|
||||
const char *condition,
|
||||
const char *text);
|
||||
|
||||
/* stanza subclasses */
|
||||
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
|
||||
const char *const type,
|
||||
const char *const to,
|
||||
const char *const id);
|
||||
const char *type,
|
||||
const char *to,
|
||||
const char *id);
|
||||
char *xmpp_message_get_body(xmpp_stanza_t *msg);
|
||||
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text);
|
||||
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *text);
|
||||
|
||||
xmpp_stanza_t *
|
||||
xmpp_iq_new(xmpp_ctx_t *ctx, const char *const type, const char *const id);
|
||||
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char *type, const char *id);
|
||||
xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx);
|
||||
xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
|
||||
xmpp_error_type_t const type,
|
||||
const char *const text);
|
||||
xmpp_stanza_t *
|
||||
xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text);
|
||||
|
||||
/* jid */
|
||||
|
||||
@@ -441,10 +633,21 @@ char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
|
||||
|
||||
/* event loop */
|
||||
|
||||
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout);
|
||||
void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout);
|
||||
void xmpp_run(xmpp_ctx_t *ctx);
|
||||
void xmpp_stop(xmpp_ctx_t *ctx);
|
||||
void xmpp_ctx_set_timeout(xmpp_ctx_t *const ctx, const unsigned long timeout);
|
||||
void xmpp_ctx_set_timeout(xmpp_ctx_t *ctx, unsigned long timeout);
|
||||
|
||||
/* TLS certificates */
|
||||
|
||||
xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert);
|
||||
xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert);
|
||||
const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert);
|
||||
const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n);
|
||||
const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
|
||||
xmpp_cert_element_t elmnt);
|
||||
const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt);
|
||||
void xmpp_tlscert_free(xmpp_tlscert_t *cert);
|
||||
|
||||
/* UUID */
|
||||
|
||||
@@ -483,6 +686,120 @@ void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
|
||||
unsigned char **out,
|
||||
size_t *outlen);
|
||||
|
||||
/* RNG */
|
||||
|
||||
typedef struct _xmpp_rand_t xmpp_rand_t;
|
||||
|
||||
/** Create new xmpp_rand_t object.
|
||||
*
|
||||
* @param ctx A Strophe context object
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
|
||||
|
||||
/** Destroy an xmpp_rand_t object.
|
||||
*
|
||||
* @param ctx A Strophe context object
|
||||
* @param rand A xmpp_rand_t object
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
|
||||
|
||||
/** Generate random integer.
|
||||
* Analogue of rand(3).
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
int xmpp_rand(xmpp_rand_t *rand);
|
||||
|
||||
/** Generate random bytes.
|
||||
* Generates len bytes and stores them to the output buffer.
|
||||
*
|
||||
* @param rand A xmpp_rand_t object
|
||||
* @param output A buffer where a len random bytes will be placed.
|
||||
* @param len Number of bytes reserved for the output..
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
|
||||
|
||||
/** Generate a nonce that is printable randomized string.
|
||||
* This function doesn't allocate memory and doesn't fail.
|
||||
*
|
||||
* @param rand A xmpp_rand_t object
|
||||
* @param output A buffer where a NULL-terminated string will be placed.
|
||||
* The string will contain len-1 printable symbols.
|
||||
* @param len Number of bytes reserved for the output string, including
|
||||
* end of line '\0'.
|
||||
*
|
||||
* @ingroup Random
|
||||
*/
|
||||
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
|
||||
|
||||
/*
|
||||
* Formerly "private but exported" functions made public for now to announce
|
||||
* deprecation */
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* XMPP_DEPRECATED(x) macro to show a compiler warning for deprecated API
|
||||
* functions
|
||||
*
|
||||
* @param x The function that can be used as a replacement or 'internal' if
|
||||
* there is no replacement
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 405)
|
||||
#define XMPP_DEPRECATED(x) __attribute__((deprecated("replaced by " #x)))
|
||||
#elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 300)
|
||||
#define XMPP_DEPRECATED(x) __attribute__((deprecated))
|
||||
#endif
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1500
|
||||
#define XMPP_DEPRECATED(x) __declspec(deprecated("replaced by " #x))
|
||||
#else
|
||||
#define XMPP_DEPRECATED(x)
|
||||
#endif
|
||||
|
||||
XMPP_DEPRECATED(internal) void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size);
|
||||
XMPP_DEPRECATED(internal)
|
||||
void *xmpp_realloc(const xmpp_ctx_t *ctx, void *p, size_t size);
|
||||
XMPP_DEPRECATED(internal)
|
||||
char *xmpp_strdup(const xmpp_ctx_t *ctx, const char *s);
|
||||
XMPP_DEPRECATED(internal)
|
||||
char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len);
|
||||
|
||||
XMPP_DEPRECATED(internal)
|
||||
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr);
|
||||
XMPP_DEPRECATED(internal)
|
||||
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...);
|
||||
XMPP_DEPRECATED(internal)
|
||||
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
|
||||
|
||||
XMPP_DEPRECATED(internal)
|
||||
void xmpp_log(const xmpp_ctx_t *ctx,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *fmt,
|
||||
va_list ap);
|
||||
XMPP_DEPRECATED(internal)
|
||||
void xmpp_error(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||
XMPP_DEPRECATED(internal)
|
||||
void xmpp_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||
XMPP_DEPRECATED(internal)
|
||||
void xmpp_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||
XMPP_DEPRECATED(internal)
|
||||
void xmpp_debug(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||
XMPP_DEPRECATED(internal)
|
||||
void xmpp_debug_verbose(
|
||||
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||
|
||||
XMPP_DEPRECATED(xmpp_conn_set_sockopt_callback)
|
||||
void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval);
|
||||
XMPP_DEPRECATED(xmpp_conn_set_flags)
|
||||
void xmpp_conn_disable_tls(xmpp_conn_t *conn);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
14
testbuild.sh
Executable file
14
testbuild.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
logfile="../../testbuild.log"
|
||||
errfile="../../testerr.log"
|
||||
|
||||
err_out() {
|
||||
tail $logfile
|
||||
exit 1
|
||||
}
|
||||
|
||||
./bootstrap.sh
|
||||
./configure >> $logfile 2>> $errfile || err_out
|
||||
make -j$(( `nproc` * 2 + 1 )) >> $logfile 2>> $errfile || err_out
|
||||
make check >> $logfile 2>> $errfile || err_out
|
||||
BIN
tests/cert.emptypass.pfx
Normal file
BIN
tests/cert.emptypass.pfx
Normal file
Binary file not shown.
BIN
tests/cert.nopass.pfx
Normal file
BIN
tests/cert.nopass.pfx
Normal file
Binary file not shown.
19
tests/cert.pem
Normal file
19
tests/cert.pem
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIDCCAqagAwIBAgIIddFxfQ2VcRIwCgYIKoZIzj0EAwQwSzFJMEcGA1UEAwxA
|
||||
dmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxlbmd0aC5pcy5hLnZhbGlk
|
||||
LmFzY2lpLmNoYXJhY3RlcjAgFw0yMzA4MDkxMzI3MDBaGA8yMTI0MDgwOTEzMjcw
|
||||
MFowSzFJMEcGA1UEAwxAdmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxl
|
||||
bmd0aC5pcy5hLnZhbGlkLmFzY2lpLmNoYXJhY3RlcjB2MBAGByqGSM49AgEGBSuB
|
||||
BAAiA2IABHfN9a5xAEPYWz+0O6Jfe95WGUeuq6Eca5po4UGcJgs78L6d4KT+FR/a
|
||||
yDSi/BmS0ZAksXbshkSjQA6XmAwWkNInJF/buAwh/hFVAOXwx3eKtguPskxg1xYj
|
||||
0WZ3WrY9oKOCAVMwggFPMAkGA1UdEwQCMAAwHQYDVR0OBBYEFNeonZqrLTGCm3Vk
|
||||
+X3TtMfDQVf4MGQGA1UdIwRdMFuhT6RNMEsxSTBHBgNVBAMMQHZlcnkubG9uZy51
|
||||
c2VybmFtZUBzby50aGUuYXNuMS5sZW5ndGguaXMuYS52YWxpZC5hc2NpaS5jaGFy
|
||||
YWN0ZXKCCHXRcX0NlXESMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD
|
||||
AjCBmgYDVR0RBIGSMIGPoE4GCCsGAQUFBwgFoEIMQHZlcnkubG9uZy51c2VybmFt
|
||||
ZUBzby50aGUuYXNuMS5sZW5ndGguaXMuYS52YWxpZC5hc2NpaS5jaGFyYWN0ZXKB
|
||||
DnlldEBub3RoZXIuc2Fugg53d3cuc3Ryb3BoZS5pbaAdBggrBgEFBQcIBaARDA9z
|
||||
ZWNvbmRAeG1wcC5qaWQwCgYIKoZIzj0EAwQDaAAwZQIxAJTzJY/q/zFws0FPPfVI
|
||||
VXc+kaGdxwzM1FKlVMwxl+PzXAoeL0Z7FtGSkaCWyaokaAIwLaYxuQhgisetYxbU
|
||||
qTyv0MdS2uUy6NNcVNztWCMS350TuuVm3f9wmwOjfaeoHft3
|
||||
-----END CERTIFICATE-----
|
||||
BIN
tests/cert.pfx
Normal file
BIN
tests/cert.pfx
Normal file
Binary file not shown.
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* check_parser.h
|
||||
** strophe XMPP client library -- parser tests
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
6
tests/key.pem
Normal file
6
tests/key.pem
Normal file
@@ -0,0 +1,6 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIGkAgEBBDBZ90AXrC5n1ZxFGq4WpqMgvH7qw2YddaUzzPFtf9qDRDrRe3xW6nTr
|
||||
Iywpxdh26GWgBwYFK4EEACKhZANiAAR3zfWucQBD2Fs/tDuiX3veVhlHrquhHGua
|
||||
aOFBnCYLO/C+neCk/hUf2sg0ovwZktGQJLF27IZEo0AOl5gMFpDSJyRf27gMIf4R
|
||||
VQDl8Md3irYLj7JMYNcWI9Fmd1q2PaA=
|
||||
-----END EC PRIVATE KEY-----
|
||||
9
tests/key_encrypted.pem
Normal file
9
tests/key_encrypted.pem
Normal file
@@ -0,0 +1,9 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,D1537075EE9323A68A08B79257D87D53
|
||||
|
||||
faIortcMN9tCcmwidAJlFDOS68OJlAH1Bzjh2sqhZDhFJEU/w3bOihTDk1cV0yg1
|
||||
JHSxolcX7aYLaBfhSdbnUDaeNhyvfF97O6lMHXbiSdkWBMKrOjSwpW5BcemQgbpI
|
||||
gjCi7KYWb2Z3CqcELR9624u4vRgytJQliqLW1jjnGMELcuq7HaoB2wP0XmXp9/BV
|
||||
QMQU504S6V8JbogFZeNGCOq+EW5C8tEBCz82y1PRUr0=
|
||||
-----END EC PRIVATE KEY-----
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* res_query_dump.c
|
||||
* Simple program to dump res_query(3) response
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* Linux and OSX:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test.c
|
||||
* strophe XMPP client library -- common routines for tests
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
@@ -16,9 +17,9 @@
|
||||
|
||||
static uint8_t char_to_bin(char c)
|
||||
{
|
||||
return c <= '9'
|
||||
? (uint8_t)(c - '0')
|
||||
: c <= 'Z' ? (uint8_t)(c - 'A' + 10) : (uint8_t)(c - 'a' + 10);
|
||||
return c <= '9' ? (uint8_t)(c - '0')
|
||||
: c <= 'Z' ? (uint8_t)(c - 'A' + 10)
|
||||
: (uint8_t)(c - 'a' + 10);
|
||||
}
|
||||
|
||||
void test_hex_to_bin(const char *hex, uint8_t *bin, size_t *bin_len)
|
||||
|
||||
67
tests/test.h
67
tests/test.h
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test.h
|
||||
** libstrophe XMPP client library -- common routines for tests
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#ifndef __LIBSTROPHE_TEST_H__
|
||||
@@ -14,6 +15,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "ostypes.h"
|
||||
|
||||
@@ -33,31 +35,48 @@
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
#endif
|
||||
|
||||
#define COMPARE(v1, v2) \
|
||||
do { \
|
||||
const char *__v1 = v1; \
|
||||
const char *__v2 = v2; \
|
||||
if (strcmp(__v1, __v2) != 0) { \
|
||||
printf("%s differs!\n" \
|
||||
"expected: %s\n" \
|
||||
"got: %s\n", \
|
||||
#v1, __v1, __v2); \
|
||||
exit(1); \
|
||||
} \
|
||||
#define COMPARE(should, is) \
|
||||
do { \
|
||||
const char *__should = should; \
|
||||
const char *__is = is; \
|
||||
if ((__should == NULL) && (__is == NULL)) { \
|
||||
/* noop */ \
|
||||
} else if (!__should || !__is || strcmp(__should, __is) != 0) { \
|
||||
printf("Error: %s\n" \
|
||||
"Expected: %s\n" \
|
||||
"Got: %s\n", \
|
||||
#should " != " #is, __should, __is); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define COMPARE_BUF(v1, len1, v2, len2) \
|
||||
do { \
|
||||
const uint8_t *__v1 = (uint8_t *)(v1); \
|
||||
const uint8_t *__v2 = (uint8_t *)(v2); \
|
||||
size_t __len1 = len1; \
|
||||
size_t __len2 = len2; \
|
||||
if (__len1 != __len2 || memcmp(__v1, __v2, __len1) != 0) { \
|
||||
printf("%s differs!\n", #v1); \
|
||||
printf("expected: 0x%s\n", test_bin_to_hex(__v1, __len1)); \
|
||||
printf("got: 0x%s\n", test_bin_to_hex(__v2, __len2)); \
|
||||
exit(1); \
|
||||
} \
|
||||
#define COMPARE_BUF(should, should_len, is, is_len) \
|
||||
do { \
|
||||
const uint8_t *__should = (uint8_t *)(should); \
|
||||
const uint8_t *__is = (uint8_t *)(is); \
|
||||
size_t __should_len = should_len; \
|
||||
size_t __is_len = is_len; \
|
||||
if (__should_len != __is_len || \
|
||||
memcmp(__should, __is, __should_len) != 0) { \
|
||||
printf("Error: %s\n", #should " != " #is); \
|
||||
printf("Expected: 0x%s\n", \
|
||||
test_bin_to_hex(__should, __should_len)); \
|
||||
printf("Got: 0x%s\n", test_bin_to_hex(__is, __is_len)); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ENSURE_EQ(should, is) \
|
||||
do { \
|
||||
int __should = should; \
|
||||
int __is = is; \
|
||||
if (__should != __is) { \
|
||||
printf("Error: %s\n" \
|
||||
"Expected: %d\n" \
|
||||
"Got: %d\n", \
|
||||
#should " != " #is, __is, __should); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void test_hex_to_bin(const char *hex, uint8_t *bin, size_t *bin_len);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_base64.c
|
||||
** libstrophe XMPP client library -- test routines for the base64 codec
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_ctx.c
|
||||
** libstrophe XMPP client library -- test routines for the library run-time
|
||||
*context
|
||||
@@ -7,7 +8,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -22,7 +23,7 @@ static int mem_alloc_called = 0;
|
||||
static int mem_free_called = 0;
|
||||
static int mem_realloc_called = 0;
|
||||
|
||||
void *my_alloc(const size_t size, void *const userdata)
|
||||
void *my_alloc(size_t size, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
|
||||
@@ -30,7 +31,7 @@ void *my_alloc(const size_t size, void *const userdata)
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void my_free(void *p, void *const userdata)
|
||||
void my_free(void *p, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
|
||||
@@ -38,7 +39,7 @@ void my_free(void *p, void *const userdata)
|
||||
return free(p);
|
||||
}
|
||||
|
||||
void *my_realloc(void *p, const size_t size, void *const userdata)
|
||||
void *my_realloc(void *p, size_t size, void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
|
||||
@@ -46,10 +47,10 @@ void *my_realloc(void *p, const size_t size, void *const userdata)
|
||||
return realloc(p, size);
|
||||
}
|
||||
|
||||
void my_logger(void *const userdata,
|
||||
const xmpp_log_level_t level,
|
||||
const char *const area,
|
||||
const char *const msg)
|
||||
void my_logger(void *userdata,
|
||||
xmpp_log_level_t level,
|
||||
const char *area,
|
||||
const char *msg)
|
||||
{
|
||||
if (strcmp((char *)userdata, "asdf") == 0 && level == XMPP_LEVEL_DEBUG &&
|
||||
strcmp(area, "test") == 0 && strcmp(msg, "hello") == 0)
|
||||
@@ -81,22 +82,22 @@ int main()
|
||||
mylog.userdata = my_str;
|
||||
|
||||
ctx = xmpp_ctx_new(&mymem, &mylog);
|
||||
xmpp_debug(ctx, "test", "hello");
|
||||
strophe_debug(ctx, "test", "hello");
|
||||
|
||||
testptr1 = xmpp_alloc(ctx, 1024);
|
||||
testptr1 = strophe_alloc(ctx, 1024);
|
||||
if (testptr1 == NULL) {
|
||||
xmpp_ctx_free(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
testptr2 = xmpp_realloc(ctx, testptr1, 2048);
|
||||
testptr2 = strophe_realloc(ctx, testptr1, 2048);
|
||||
if (testptr2 == NULL) {
|
||||
xmpp_free(ctx, testptr1);
|
||||
strophe_free(ctx, testptr1);
|
||||
xmpp_ctx_free(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_free(ctx, testptr2);
|
||||
strophe_free(ctx, testptr2);
|
||||
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
|
||||
49
tests/test_fuzz_parser.c
Normal file
49
tests/test_fuzz_parser.c
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "parser.h"
|
||||
|
||||
void xmpp_initialize(void);
|
||||
|
||||
void cbtest_handle_start(char *name, char **attrs, void *userdata)
|
||||
{
|
||||
(void)name;
|
||||
(void)attrs;
|
||||
(void)userdata;
|
||||
}
|
||||
|
||||
void cbtest_handle_end(char *name, void *userdata)
|
||||
{
|
||||
(void)name;
|
||||
(void)userdata;
|
||||
}
|
||||
|
||||
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
(void)stanza;
|
||||
(void)userdata;
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
parser_t *parser;
|
||||
|
||||
char *dup = malloc(Size);
|
||||
memcpy(dup, Data, Size);
|
||||
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
parser = parser_new(ctx, cbtest_handle_start, cbtest_handle_end,
|
||||
cbtest_handle_stanza, NULL);
|
||||
|
||||
parser_feed(parser, dup, Size);
|
||||
|
||||
free(dup);
|
||||
parser_free(parser);
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
63
tests/test_fuzz_resolver.c
Normal file
63
tests/test_fuzz_resolver.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "resolver.h"
|
||||
|
||||
void xmpp_initialize(void);
|
||||
|
||||
void cbtest_handle_start(char *name, char **attrs, void *userdata)
|
||||
{
|
||||
(void)name;
|
||||
(void)attrs;
|
||||
(void)userdata;
|
||||
}
|
||||
|
||||
void cbtest_handle_end(char *name, void *userdata)
|
||||
{
|
||||
(void)name;
|
||||
(void)userdata;
|
||||
}
|
||||
|
||||
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
(void)stanza;
|
||||
(void)userdata;
|
||||
}
|
||||
|
||||
/* res_query("_xmpp-client._tcp.jabber.org", C_IN, T_SRV, ...) */
|
||||
static const unsigned char data2[] = {
|
||||
0xf2, 0x98, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65,
|
||||
0x72, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c,
|
||||
0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1a, 0x00, 0x1e,
|
||||
0x00, 0x1e, 0x14, 0x66, 0x07, 0x68, 0x65, 0x72, 0x6d, 0x65, 0x73, 0x32,
|
||||
0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, 0x6f, 0x72, 0x67, 0x00,
|
||||
0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1c,
|
||||
0x00, 0x1f, 0x00, 0x1e, 0x14, 0x66, 0x09, 0x68, 0x65, 0x72, 0x6d, 0x65,
|
||||
0x73, 0x32, 0x76, 0x36, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03,
|
||||
0x6f, 0x72, 0x67, 0x00,
|
||||
};
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
resolver_srv_rr_t *srv_rr_list;
|
||||
|
||||
unsigned char *dup = malloc(Size + 64);
|
||||
memcpy(dup, data2, 64);
|
||||
memcpy(&dup[64], Data, Size);
|
||||
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
resolver_srv_lookup_buf(ctx, dup, 64 + Size, &srv_rr_list);
|
||||
if (srv_rr_list != NULL)
|
||||
resolver_srv_free(ctx, srv_rr_list);
|
||||
|
||||
free(dup);
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_hash.c
|
||||
** libstrophe XMPP client library -- self-test for the hash-table implementation
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -55,7 +56,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* allocate a hash table */
|
||||
table = hash_new(ctx, TABLESIZE, xmpp_free);
|
||||
table = hash_new(ctx, TABLESIZE, strophe_free);
|
||||
if (table == NULL) {
|
||||
/* table allocation failed! */
|
||||
return 1;
|
||||
@@ -63,7 +64,7 @@ int main(int argc, char **argv)
|
||||
|
||||
/* test insertion */
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
err = hash_add(table, keys[i], xmpp_strdup(ctx, values[i]));
|
||||
err = hash_add(table, keys[i], strophe_strdup(ctx, values[i]));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@@ -76,7 +77,7 @@ int main(int argc, char **argv)
|
||||
|
||||
/* test replacing old values */
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
err = hash_add(table, keys[0], xmpp_strdup(ctx, values[i]));
|
||||
err = hash_add(table, keys[0], strophe_strdup(ctx, values[i]));
|
||||
if (err)
|
||||
return err;
|
||||
if (hash_num_keys(table) != nkeys)
|
||||
@@ -88,7 +89,7 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
/* restore value for the 1st key */
|
||||
hash_add(table, keys[0], xmpp_strdup(ctx, values[0]));
|
||||
hash_add(table, keys[0], strophe_strdup(ctx, values[0]));
|
||||
|
||||
/* test cloning */
|
||||
clone = hash_clone(table);
|
||||
|
||||
176
tests/test_jid.c
176
tests/test_jid.c
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_jid.c
|
||||
** libstrophe XMPP client library -- test routines for the jid utils
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -15,114 +16,80 @@
|
||||
#include "strophe.h"
|
||||
#include "common.h"
|
||||
|
||||
static const char jid1[] = "foo@bar.com";
|
||||
static const char jid2[] = "anyone@example.com/hullo";
|
||||
static const char jid3[] = "manic.porter@xyz.net/frob";
|
||||
static const char jid4[] = "domain.tld";
|
||||
#include "test.h"
|
||||
|
||||
static const char *_s(const char *s)
|
||||
{
|
||||
return s == NULL ? "<NULL>" : s;
|
||||
}
|
||||
|
||||
int test_jid(xmpp_ctx_t *ctx)
|
||||
static int test_jid(xmpp_ctx_t *ctx)
|
||||
{
|
||||
char *bare;
|
||||
char *node;
|
||||
char *domain;
|
||||
char *resource;
|
||||
size_t n;
|
||||
struct {
|
||||
const char *jid;
|
||||
const char *bare;
|
||||
const char *node;
|
||||
const char *domain;
|
||||
const char *resource;
|
||||
} testcases[] = {
|
||||
{"foo@bar.com", "foo@bar.com", "foo", "bar.com", NULL},
|
||||
{
|
||||
"anyone@example.com/hullo",
|
||||
"anyone@example.com",
|
||||
"anyone",
|
||||
"example.com",
|
||||
"hullo",
|
||||
},
|
||||
{
|
||||
"a.example.com/b@example.net",
|
||||
"a.example.com",
|
||||
NULL,
|
||||
"a.example.com",
|
||||
"b@example.net",
|
||||
},
|
||||
{
|
||||
"manic.porter@xyz.net/frob",
|
||||
"manic.porter@xyz.net",
|
||||
"manic.porter",
|
||||
"xyz.net",
|
||||
"frob",
|
||||
},
|
||||
{
|
||||
"domain.tld",
|
||||
"domain.tld",
|
||||
NULL,
|
||||
"domain.tld",
|
||||
NULL,
|
||||
},
|
||||
};
|
||||
|
||||
bare = xmpp_jid_bare(ctx, jid1);
|
||||
node = xmpp_jid_node(ctx, jid1);
|
||||
domain = xmpp_jid_domain(ctx, jid1);
|
||||
resource = xmpp_jid_resource(ctx, jid1);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n", jid1, _s(node), _s(domain),
|
||||
_s(resource));
|
||||
if (bare == NULL || strcmp(bare, "foo@bar.com"))
|
||||
return 1;
|
||||
if (node == NULL || strcmp(node, "foo"))
|
||||
return 1;
|
||||
if (domain == NULL || strcmp(domain, "bar.com"))
|
||||
return 1;
|
||||
if (resource != NULL)
|
||||
return 1;
|
||||
if (bare)
|
||||
xmpp_free(ctx, bare);
|
||||
if (node)
|
||||
xmpp_free(ctx, node);
|
||||
if (domain)
|
||||
xmpp_free(ctx, domain);
|
||||
if (resource)
|
||||
xmpp_free(ctx, resource);
|
||||
for (n = 0; n < sizeof(testcases) / sizeof(testcases[0]); ++n) {
|
||||
bare = xmpp_jid_bare(ctx, testcases[n].jid);
|
||||
node = xmpp_jid_node(ctx, testcases[n].jid);
|
||||
domain = xmpp_jid_domain(ctx, testcases[n].jid);
|
||||
resource = xmpp_jid_resource(ctx, testcases[n].jid);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n", testcases[n].jid, _s(node),
|
||||
_s(domain), _s(resource));
|
||||
COMPARE(testcases[n].bare, bare);
|
||||
COMPARE(testcases[n].node, node);
|
||||
COMPARE(testcases[n].domain, domain);
|
||||
COMPARE(testcases[n].resource, resource);
|
||||
if (bare)
|
||||
strophe_free(ctx, bare);
|
||||
if (node)
|
||||
strophe_free(ctx, node);
|
||||
if (domain)
|
||||
strophe_free(ctx, domain);
|
||||
if (resource)
|
||||
strophe_free(ctx, resource);
|
||||
}
|
||||
|
||||
bare = xmpp_jid_bare(ctx, jid2);
|
||||
node = xmpp_jid_node(ctx, jid2);
|
||||
domain = xmpp_jid_domain(ctx, jid2);
|
||||
resource = xmpp_jid_resource(ctx, jid2);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n", jid2, _s(node), _s(domain),
|
||||
_s(resource));
|
||||
if (bare == NULL || strcmp(bare, "anyone@example.com"))
|
||||
return 1;
|
||||
if (node == NULL || strcmp(node, "anyone"))
|
||||
return 1;
|
||||
if (domain == NULL || strcmp(domain, "example.com"))
|
||||
return 1;
|
||||
if (resource == NULL || strcmp(resource, "hullo"))
|
||||
return 1;
|
||||
if (bare)
|
||||
xmpp_free(ctx, bare);
|
||||
if (node)
|
||||
xmpp_free(ctx, node);
|
||||
if (domain)
|
||||
xmpp_free(ctx, domain);
|
||||
if (resource)
|
||||
xmpp_free(ctx, resource);
|
||||
|
||||
bare = xmpp_jid_bare(ctx, jid3);
|
||||
node = xmpp_jid_node(ctx, jid3);
|
||||
domain = xmpp_jid_domain(ctx, jid3);
|
||||
resource = xmpp_jid_resource(ctx, jid3);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n", jid3, _s(node), _s(domain),
|
||||
_s(resource));
|
||||
if (bare == NULL || strcmp(bare, "manic.porter@xyz.net"))
|
||||
return 1;
|
||||
if (node == NULL || strcmp(node, "manic.porter"))
|
||||
return 1;
|
||||
if (domain == NULL || strcmp(domain, "xyz.net"))
|
||||
return 1;
|
||||
if (resource == NULL || strcmp(resource, "frob"))
|
||||
return 1;
|
||||
if (bare)
|
||||
xmpp_free(ctx, bare);
|
||||
if (node)
|
||||
xmpp_free(ctx, node);
|
||||
if (domain)
|
||||
xmpp_free(ctx, domain);
|
||||
if (resource)
|
||||
xmpp_free(ctx, resource);
|
||||
|
||||
bare = xmpp_jid_bare(ctx, jid4);
|
||||
node = xmpp_jid_node(ctx, jid4);
|
||||
domain = xmpp_jid_domain(ctx, jid4);
|
||||
resource = xmpp_jid_resource(ctx, jid4);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n", jid4, _s(node), _s(domain),
|
||||
_s(resource));
|
||||
if (bare == NULL || strcmp(bare, "domain.tld"))
|
||||
return 1;
|
||||
if (node != NULL)
|
||||
return 1;
|
||||
if (domain == NULL || strcmp(domain, "domain.tld"))
|
||||
return 1;
|
||||
if (resource != NULL)
|
||||
return 1;
|
||||
if (bare)
|
||||
xmpp_free(ctx, bare);
|
||||
if (node)
|
||||
xmpp_free(ctx, node);
|
||||
if (domain)
|
||||
xmpp_free(ctx, domain);
|
||||
if (resource)
|
||||
xmpp_free(ctx, resource);
|
||||
printf("test_jid() finished successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -135,13 +102,26 @@ int test_jid_new(xmpp_ctx_t *ctx)
|
||||
printf("new jid: '%s'\n", jid);
|
||||
if (strcmp(jid, "node@domain/resource"))
|
||||
return 1;
|
||||
xmpp_free(ctx, jid);
|
||||
strophe_free(ctx, jid);
|
||||
|
||||
jid = xmpp_jid_new(ctx, "foo", "bar.com", NULL);
|
||||
printf("new jid: '%s'\n", jid);
|
||||
if (strcmp(jid, "foo@bar.com"))
|
||||
return 1;
|
||||
xmpp_free(ctx, jid);
|
||||
strophe_free(ctx, jid);
|
||||
|
||||
const char *invalid_chars = "\"&'/:<>@";
|
||||
char localpart[] = "localpart";
|
||||
do {
|
||||
localpart[1] = *invalid_chars;
|
||||
jid = xmpp_jid_new(ctx, localpart, "bar.com", NULL);
|
||||
if (jid != NULL) {
|
||||
printf("Shouldn't have created JID with localpart=\"%s\"\n",
|
||||
localpart);
|
||||
return 1;
|
||||
}
|
||||
invalid_chars++;
|
||||
} while (*invalid_chars != '\0');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_md5.c
|
||||
* strophe XMPP client library -- test vectors for MD5
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* gcc -o test_md5 -I./src tests/test_md5.c tests/test.c src/md5.c */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* check_rand.c
|
||||
* strophe XMPP client library -- test vectors for Hash_DRBG
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* gcc -o test_rand -I./src tests/test_rand.c tests/test.c src/sha1.c */
|
||||
@@ -21,32 +22,36 @@
|
||||
#include "rand.c"
|
||||
|
||||
/* stubs to build test without whole libstrophe */
|
||||
void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size)
|
||||
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)size;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void xmpp_free(const xmpp_ctx_t *const ctx, void *p)
|
||||
void strophe_free(const xmpp_ctx_t *ctx, void *p)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)p;
|
||||
}
|
||||
|
||||
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
|
||||
#ifndef HAVE_SNPRINTF
|
||||
int strophe_snprintf(char *str, size_t count, const char *fmt, ...)
|
||||
{
|
||||
(void)str;
|
||||
(void)count;
|
||||
(void)fmt;
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_SNPRINTF */
|
||||
|
||||
uint64_t time_stamp(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef USE_GETRANDOM
|
||||
|
||||
static struct {
|
||||
const char *entropy_input;
|
||||
const char *nonce;
|
||||
@@ -158,3 +163,19 @@ int main()
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main()
|
||||
{
|
||||
uint8_t output[1024];
|
||||
xmpp_rand_t *rand = xmpp_rand_new(NULL);
|
||||
|
||||
assert(rand != NULL);
|
||||
/* this would assert if it failed */
|
||||
xmpp_rand_bytes(rand, output, sizeof(output));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_resolver.c
|
||||
* strophe XMPP client library -- tests for resolver
|
||||
*
|
||||
@@ -6,14 +7,13 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "rand.h"
|
||||
#include "resolver.h"
|
||||
#include "test.h"
|
||||
|
||||
@@ -175,10 +175,10 @@ int main()
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_rand_t *rand;
|
||||
resolver_srv_rr_t *srv_rr_list;
|
||||
char *domain;
|
||||
char *domain, *rnd_b64;
|
||||
unsigned char *buf;
|
||||
unsigned short port;
|
||||
size_t i;
|
||||
size_t i, slen;
|
||||
int ret;
|
||||
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
@@ -217,17 +217,25 @@ int main()
|
||||
rand = xmpp_rand_new(ctx);
|
||||
assert(rand != NULL);
|
||||
assert(sizeof(data2) > 64);
|
||||
buf = xmpp_alloc(ctx, sizeof(data2));
|
||||
buf = strophe_alloc(ctx, sizeof(data2));
|
||||
assert(buf != NULL);
|
||||
memcpy(buf, data2, 64);
|
||||
xmpp_rand_bytes(rand, &buf[64], sizeof(data2) - 64);
|
||||
ret = resolver_srv_lookup_buf(ctx, buf, sizeof(data2), &srv_rr_list);
|
||||
if (ret == XMPP_DOMAIN_FOUND && srv_rr_list != NULL)
|
||||
resolver_srv_free(ctx, srv_rr_list);
|
||||
xmpp_free(ctx, buf);
|
||||
xmpp_rand_free(ctx, rand);
|
||||
printf("ok\n");
|
||||
|
||||
printf("Broken message was:\n");
|
||||
rnd_b64 = xmpp_base64_encode(ctx, buf, sizeof(data2));
|
||||
slen = strlen(rnd_b64);
|
||||
for (i = 0; i < slen; i += 64) {
|
||||
printf("%.64s\n", &rnd_b64[i]);
|
||||
}
|
||||
|
||||
strophe_free(ctx, buf);
|
||||
strophe_free(ctx, rnd_b64);
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_sasl.c
|
||||
** libstrophe XMPP client library -- test routines for the SASL implementation
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -29,23 +30,6 @@ static const char response_md5[] =
|
||||
"MDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL3NvbWVyZWFsbSIscmVz"
|
||||
"cG9uc2U9NGVhNmU4N2JjMDkzMzUwNzQzZGIyOGQ3MDIwOGNhZmIsY2hhcnNl"
|
||||
"dD11dGYtOA==";
|
||||
static const char response_md5_rfc[] =
|
||||
"dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i"
|
||||
"T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw"
|
||||
"MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i"
|
||||
"LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo"
|
||||
"YXJzZXQ9dXRmLTgK";
|
||||
|
||||
static const char challenge_md5_2[] =
|
||||
"cmVhbG09ImVsd29vZC5pbm5vc29mdC5jb20iLG5vbmNlPSJPQTZNRzl0"
|
||||
"RVFHbTJoaCIscW9wPSJhdXRoIixhbGdvcml0aG09bWQ1LXNlc3MsY2hh"
|
||||
"cnNldD11dGYtOA==";
|
||||
static const char response_md5_2[] =
|
||||
"Y2hhcnNldD11dGYtOCx1c2VybmFtZT0iY2hyaXMiLHJlYWxtPSJlbHdvb2"
|
||||
"QuaW5ub3NvZnQuY29tIixub25jZT0iT0E2TUc5dEVRR20yaGgiLG5jPTAw"
|
||||
"MDAwMDAxLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLGRpZ2VzdC11cmk9Im"
|
||||
"ltYXAvZWx3b29kLmlubm9zb2Z0LmNvbSIscmVzcG9uc2U9ZDM4OGRhZDkw"
|
||||
"ZDRiYmQ3NjBhMTUyMzIxZjIxNDNhZjcscW9wPWF1dGg=";
|
||||
|
||||
int test_plain(xmpp_ctx_t *ctx)
|
||||
{
|
||||
@@ -60,7 +44,7 @@ int test_plain(xmpp_ctx_t *ctx)
|
||||
/* SASL PLAIN returned incorrect string! */
|
||||
return 2;
|
||||
}
|
||||
xmpp_free(ctx, result);
|
||||
strophe_free(ctx, result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_scram.c
|
||||
* strophe XMPP client library -- test vectors for SCRAM-SHA1
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* gcc -o test_scram -I./src tests/test_scram.c tests/test.c src/sha1.c */
|
||||
|
||||
93
tests/test_send_queue.c
Normal file
93
tests/test_send_queue.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_send_queue.c
|
||||
** libstrophe XMPP client library -- test routines for the send queue
|
||||
**
|
||||
** Copyright (C) 2021 Steffen Jaeckel
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "common.h"
|
||||
|
||||
#include "test.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
xmpp_log_t *log;
|
||||
xmpp_conn_state_t state;
|
||||
xmpp_sm_state_t *sm_state;
|
||||
char *ret;
|
||||
|
||||
unsigned int n;
|
||||
|
||||
xmpp_initialize();
|
||||
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||
ctx = xmpp_ctx_new(NULL, log);
|
||||
conn = xmpp_conn_new(ctx);
|
||||
sm_state = strophe_alloc(ctx, sizeof(*sm_state));
|
||||
memset(sm_state, 0, sizeof(*sm_state));
|
||||
sm_state->ctx = ctx;
|
||||
|
||||
xmpp_conn_set_sm_state(conn, sm_state);
|
||||
|
||||
ENSURE_EQ(0, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
state = conn->state;
|
||||
conn->state = XMPP_STATE_CONNECTED;
|
||||
|
||||
xmpp_send_raw(conn, "foo", 3);
|
||||
ENSURE_EQ(1, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
xmpp_send_raw(conn, "bar", 3);
|
||||
ENSURE_EQ(2, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
xmpp_send_raw(conn, "baz", 3);
|
||||
ENSURE_EQ(3, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
xmpp_send_raw(conn, "baan", 4);
|
||||
ENSURE_EQ(4, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
conn->send_queue_head->wip = 1;
|
||||
ENSURE_EQ(3, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_OLDEST);
|
||||
COMPARE("bar", ret);
|
||||
xmpp_free(ctx, ret);
|
||||
ENSURE_EQ(2, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
conn->send_queue_head->wip = 0;
|
||||
ENSURE_EQ(3, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_OLDEST);
|
||||
COMPARE("foo", ret);
|
||||
xmpp_free(ctx, ret);
|
||||
ENSURE_EQ(2, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_YOUNGEST);
|
||||
COMPARE("baan", ret);
|
||||
xmpp_free(ctx, ret);
|
||||
ENSURE_EQ(1, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_YOUNGEST);
|
||||
COMPARE("baz", ret);
|
||||
xmpp_free(ctx, ret);
|
||||
ENSURE_EQ(0, xmpp_conn_send_queue_len(conn));
|
||||
|
||||
conn->state = state;
|
||||
|
||||
xmpp_conn_release(conn);
|
||||
xmpp_ctx_free(ctx);
|
||||
xmpp_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
166
tests/test_serialize_sm.c
Normal file
166
tests/test_serialize_sm.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_serialize_sm.c
|
||||
** libstrophe XMPP client library -- test routines for the send queue
|
||||
**
|
||||
** Copyright (C) 2024 Stephen Paul Weber
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "common.h"
|
||||
|
||||
#include "test.h"
|
||||
|
||||
void callback(xmpp_conn_t *conn,
|
||||
void *ctx,
|
||||
const unsigned char *sm_state,
|
||||
size_t sm_state_len)
|
||||
{
|
||||
int *callback_count = ctx;
|
||||
(*callback_count)++;
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state, 5);
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 10, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x04SMID", 9, sm_state + 15, 9);
|
||||
if (*callback_count == 1) {
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 5, 5);
|
||||
COMPARE_BUF("\x9a\x00\x00\x00\x02", 5, sm_state + 24, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x03"
|
||||
"foo",
|
||||
8, sm_state + 29, 8);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31,
|
||||
sm_state + 37, 31);
|
||||
COMPARE_BUF("\xba\x00\x00\x00\x00", 5, sm_state + 68, 5);
|
||||
ENSURE_EQ(sm_state_len, 73);
|
||||
|
||||
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
|
||||
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
|
||||
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 0);
|
||||
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
|
||||
COMPARE(newconn->sm_state->id, "SMID");
|
||||
ENSURE_EQ(newconn->send_queue_len, 2);
|
||||
ENSURE_EQ(newconn->send_queue_user_len, 2);
|
||||
ENSURE_EQ((size_t)(newconn->sm_state->sm_queue.head), 0);
|
||||
xmpp_conn_release(newconn);
|
||||
}
|
||||
if (*callback_count == 2) {
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state + 5, 5);
|
||||
COMPARE_BUF("\x9a\x00\x00\x00\x01", 5, sm_state + 24, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31,
|
||||
sm_state + 29, 31);
|
||||
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state + 60, 5);
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 65, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x03"
|
||||
"foo",
|
||||
8, sm_state + 70, 8);
|
||||
ENSURE_EQ(sm_state_len, 78);
|
||||
|
||||
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
|
||||
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
|
||||
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
|
||||
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
|
||||
COMPARE(newconn->sm_state->id, "SMID");
|
||||
ENSURE_EQ(newconn->send_queue_len, 1);
|
||||
ENSURE_EQ(newconn->send_queue_user_len, 1);
|
||||
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
|
||||
xmpp_conn_release(newconn);
|
||||
}
|
||||
if (*callback_count == 3) {
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state + 5, 5);
|
||||
COMPARE_BUF("\x9a\x00\x00\x00\x00", 5, sm_state + 24, 5);
|
||||
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state + 29, 5);
|
||||
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 34, 5);
|
||||
COMPARE_BUF("\x7a\x00\x00\x00\x03"
|
||||
"foo",
|
||||
8, sm_state + 39, 8);
|
||||
ENSURE_EQ(sm_state_len, 47);
|
||||
|
||||
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
|
||||
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
|
||||
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
|
||||
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
|
||||
COMPARE(newconn->sm_state->id, "SMID");
|
||||
ENSURE_EQ(newconn->send_queue_len, 0);
|
||||
ENSURE_EQ(newconn->send_queue_user_len, 0);
|
||||
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
|
||||
xmpp_conn_release(newconn);
|
||||
}
|
||||
}
|
||||
|
||||
int fake_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fake_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
int fake_flush(struct conn_interface *intf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fake_error_is_recoverable(struct conn_interface *intf, int err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
xmpp_log_t *log;
|
||||
xmpp_conn_state_t state;
|
||||
xmpp_sm_state_t *sm_state;
|
||||
char *ret;
|
||||
unsigned int n;
|
||||
int callback_count = 0;
|
||||
|
||||
xmpp_initialize();
|
||||
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||
ctx = xmpp_ctx_new(NULL, log);
|
||||
conn = xmpp_conn_new(ctx);
|
||||
sm_state = strophe_alloc(ctx, sizeof(*sm_state));
|
||||
memset(sm_state, 0, sizeof(*sm_state));
|
||||
sm_state->ctx = ctx;
|
||||
sm_state->sm_support = sm_state->sm_enabled = sm_state->can_resume = 1;
|
||||
sm_state->id = strophe_strdup(ctx, "SMID");
|
||||
|
||||
xmpp_conn_set_sm_state(conn, sm_state);
|
||||
xmpp_conn_set_sm_callback(conn, callback, &callback_count);
|
||||
|
||||
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 0);
|
||||
|
||||
struct conn_interface intf = {fake_read, fake_write,
|
||||
fake_flush, fake_flush,
|
||||
fake_flush, fake_error_is_recoverable,
|
||||
conn};
|
||||
conn->intf = intf;
|
||||
state = conn->state;
|
||||
conn->state = XMPP_STATE_CONNECTED;
|
||||
conn->sock = 123;
|
||||
|
||||
xmpp_send_raw(conn, "foo", 3);
|
||||
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 1);
|
||||
ENSURE_EQ(callback_count, 1);
|
||||
|
||||
xmpp_run_once(ctx, 0);
|
||||
ENSURE_EQ(callback_count, 3);
|
||||
|
||||
conn->state = state;
|
||||
|
||||
xmpp_conn_release(conn);
|
||||
xmpp_ctx_free(ctx);
|
||||
xmpp_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#undef HAVE_VSNPRINTF
|
||||
#undef HAVE_SNPRINTF
|
||||
@@ -28,7 +29,14 @@ int main(void)
|
||||
char *int_fmt[] = {"%-1.5d", "%1.5d", "%123.9d", "%5.5d",
|
||||
"%10.5d", "% 10.5d", "%+22.33d", "%01.3d",
|
||||
"%4d", "0x%x", "0x%04x", NULL};
|
||||
long int_nums[] = {-1, 134, 91340, 341, 0203, 0x76543210, 0};
|
||||
int int_nums[] = {-1, 134, 91340, 341, 0203,
|
||||
0x76543210, INT_MIN, INT_MAX, 0};
|
||||
char *long_fmt[] = {"%-1.5ld", "%1.5ld", "%123.9ld", "%5.5ld",
|
||||
"%10.5ld", "% 10.5ld", "%+22.33ld", "%01.3ld",
|
||||
"%4ld", "0x%lx", "0x%04lx", NULL};
|
||||
long long_nums[] = {-1L, 134L, 91340L,
|
||||
341L, 0203L, 0xFEDCBA9876543210L,
|
||||
LONG_MIN, LONG_MAX, 0L};
|
||||
int x, y;
|
||||
int fail = 0;
|
||||
int num = 0;
|
||||
@@ -37,7 +45,7 @@ int main(void)
|
||||
|
||||
for (x = 0; fp_fmt[x] != NULL; x++)
|
||||
for (y = 0; fp_nums[y] != 0; y++) {
|
||||
xmpp_snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
|
||||
strophe_snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
|
||||
sprintf(buf2, fp_fmt[x], fp_nums[y]);
|
||||
if (strcmp(buf1, buf2)) {
|
||||
printf("xmpp_snprintf doesn't match Format: "
|
||||
@@ -50,7 +58,7 @@ int main(void)
|
||||
|
||||
for (x = 0; int_fmt[x] != NULL; x++)
|
||||
for (y = 0; int_nums[y] != 0; y++) {
|
||||
xmpp_snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
|
||||
strophe_snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
|
||||
sprintf(buf2, int_fmt[x], int_nums[y]);
|
||||
if (strcmp(buf1, buf2)) {
|
||||
printf("xmpp_snprintf doesn't match Format: "
|
||||
@@ -60,6 +68,19 @@ int main(void)
|
||||
}
|
||||
num++;
|
||||
}
|
||||
|
||||
for (x = 0; long_fmt[x] != NULL; x++)
|
||||
for (y = 0; long_nums[y] != 0; y++) {
|
||||
strophe_snprintf(buf1, sizeof(buf1), long_fmt[x], long_nums[y]);
|
||||
sprintf(buf2, long_fmt[x], long_nums[y]);
|
||||
if (strcmp(buf1, buf2)) {
|
||||
printf("xmpp_snprintf doesn't match Format: "
|
||||
"%s\n\txmpp_snprintf = %s\n\tsprintf = %s\n",
|
||||
long_fmt[x], buf1, buf2);
|
||||
fail++;
|
||||
}
|
||||
num++;
|
||||
}
|
||||
printf("%d tests failed out of %d.\n", fail, num);
|
||||
return fail != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_sock.c
|
||||
** libstrophe XMPP client library -- test routines for the socket abstraction
|
||||
**
|
||||
@@ -6,7 +7,7 @@
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||
/* test_stanza.c
|
||||
* libstrophe XMPP client library -- test routines for stanza functions
|
||||
*
|
||||
@@ -6,7 +7,7 @@
|
||||
* This software is provided AS-IS with no warranty, either express
|
||||
* or implied.
|
||||
*
|
||||
* This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/* gcc -o test_stanza -I./src tests/test_stanza.c -lstrophe */
|
||||
@@ -17,6 +18,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#define MAGICPTR ((void *)0xfeedbeef)
|
||||
static unsigned long used_blocks = 0;
|
||||
|
||||
@@ -97,7 +100,7 @@ static void test_stanza_from_string(xmpp_ctx_t *ctx)
|
||||
size_t buflen;
|
||||
int ret;
|
||||
|
||||
static const char *str =
|
||||
const char *str =
|
||||
"<signcrypt xmlns=\"urn:xmpp:openpgp:0\"><to "
|
||||
"jid=\"user@domain.com\"/><time "
|
||||
"stamp=\"2020-06-03T21:26:24+0200\"/><rpad/><payload><body "
|
||||
@@ -107,9 +110,137 @@ static void test_stanza_from_string(xmpp_ctx_t *ctx)
|
||||
assert(stanza != NULL);
|
||||
ret = xmpp_stanza_to_text(stanza, &buf, &buflen);
|
||||
assert(ret == XMPP_EOK);
|
||||
assert(strcmp(buf, str) == 0);
|
||||
COMPARE(str, buf);
|
||||
xmpp_free(ctx, buf);
|
||||
xmpp_stanza_release(stanza);
|
||||
|
||||
/* create a string with two stanzas to make sure we don't
|
||||
* leak any memory when we convert them to a xmpp_stanza_t
|
||||
*/
|
||||
buf = malloc(strlen(str) * 2 + 1);
|
||||
assert(buf != NULL);
|
||||
memcpy(buf, str, strlen(str) + 1);
|
||||
memcpy(&buf[strlen(str)], str, strlen(str) + 1);
|
||||
stanza = xmpp_stanza_new_from_string(ctx, buf);
|
||||
assert(stanza != NULL);
|
||||
free(buf);
|
||||
ret = xmpp_stanza_to_text(stanza, &buf, &buflen);
|
||||
assert(ret == XMPP_EOK);
|
||||
COMPARE(str, buf);
|
||||
xmpp_free(ctx, buf);
|
||||
xmpp_stanza_release(stanza);
|
||||
|
||||
/* Error path. */
|
||||
stanza = xmpp_stanza_new_from_string(ctx, "<uu><uu>tt");
|
||||
assert(stanza == NULL);
|
||||
}
|
||||
|
||||
static void test_stanza_error(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_stanza_t *stanza;
|
||||
xmpp_stanza_t *error;
|
||||
xmpp_stanza_t *item;
|
||||
xmpp_stanza_t *mood;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
const char *attr[10];
|
||||
int attrlen = ARRAY_SIZE(attr);
|
||||
int ret;
|
||||
|
||||
static const char *str =
|
||||
"<iq from='romeo@montague.lit/home' to='juliet@capulet.lit/chamber' "
|
||||
"type='get' id='e2e1'><ping xmlns='urn:xmpp:ping'/></iq>";
|
||||
static const char *str_error =
|
||||
"<error type=\"cancel\"><service-unavailable "
|
||||
"xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/></error>";
|
||||
// clang-format off
|
||||
static const char *str_mood =
|
||||
"<iq from='juliet@capulet.lit/balcony' id='publish1' type='set'>"
|
||||
"<pubsub xmlns='http://jabber.org/protocol/pubsub'>"
|
||||
"<publish node='http://jabber.org/protocol/mood'>"
|
||||
"<item>"
|
||||
"<mood xmlns='http://jabber.org/protocol/mood'>"
|
||||
"<annoyed/>"
|
||||
"<text>curse my nurse!</text>"
|
||||
"</mood>"
|
||||
"</item>"
|
||||
"</publish>"
|
||||
"</pubsub>"
|
||||
"</iq>";
|
||||
// clang-format on
|
||||
|
||||
stanza = xmpp_stanza_new_from_string(ctx, str);
|
||||
assert(stanza != NULL);
|
||||
error =
|
||||
xmpp_stanza_reply_error(stanza, "cancel", "service-unavailable", NULL);
|
||||
assert(error != NULL);
|
||||
mood = xmpp_stanza_new_from_string(ctx, str_mood);
|
||||
assert(mood != NULL);
|
||||
|
||||
assert(xmpp_stanza_get_to(error) != NULL);
|
||||
COMPARE("romeo@montague.lit/home", xmpp_stanza_get_to(error));
|
||||
assert(xmpp_stanza_get_from(error) != NULL);
|
||||
COMPARE("juliet@capulet.lit/chamber", xmpp_stanza_get_from(error));
|
||||
assert(xmpp_stanza_get_id(error) != NULL);
|
||||
COMPARE("e2e1", xmpp_stanza_get_id(error));
|
||||
assert(xmpp_stanza_get_type(error) != NULL);
|
||||
COMPARE("error", xmpp_stanza_get_type(error));
|
||||
|
||||
/* FAIL - no list given */
|
||||
item = xmpp_stanza_get_child_by_path(mood, NULL);
|
||||
assert(item == NULL);
|
||||
|
||||
/* FAIL - first entry doesn't match */
|
||||
item = xmpp_stanza_get_child_by_path(mood, "foo", NULL);
|
||||
assert(item == NULL);
|
||||
|
||||
/* FAIL - 'iq' has no namespace */
|
||||
item = xmpp_stanza_get_child_by_path(
|
||||
mood, XMPP_STANZA_NAME_IN_NS("iq", "foobar"),
|
||||
XMPP_STANZA_NAME_IN_NS("pubsub", "http://jabber.org/protocol/pubsub"),
|
||||
"publish", "item", "mood", NULL);
|
||||
assert(item == NULL);
|
||||
|
||||
/* FAIL - 'pubsub' is in another namespace */
|
||||
item = xmpp_stanza_get_child_by_path(
|
||||
mood, "iq",
|
||||
XMPP_STANZA_NAME_IN_NS("pubsub", "http://jabber.org/protocol/foobar"),
|
||||
"publish", "item", "mood", NULL);
|
||||
assert(item == NULL);
|
||||
|
||||
item = xmpp_stanza_get_child_by_path(mood, "iq", "pubsub", "publish",
|
||||
"item", "mood", NULL);
|
||||
assert(item != NULL);
|
||||
assert(xmpp_stanza_get_children(item) != NULL);
|
||||
assert(xmpp_stanza_get_name(xmpp_stanza_get_children(item)) != NULL);
|
||||
COMPARE("annoyed", xmpp_stanza_get_name(xmpp_stanza_get_children(item)));
|
||||
|
||||
item = xmpp_stanza_get_child_by_path(
|
||||
mood, "iq",
|
||||
XMPP_STANZA_NAME_IN_NS("pubsub", "http://jabber.org/protocol/pubsub"),
|
||||
"publish", "item",
|
||||
XMPP_STANZA_NAME_IN_NS("mood", "http://jabber.org/protocol/mood"),
|
||||
NULL);
|
||||
assert(item != NULL);
|
||||
assert(xmpp_stanza_get_children(item) != NULL);
|
||||
assert(xmpp_stanza_get_name(xmpp_stanza_get_children(item)) != NULL);
|
||||
COMPARE("annoyed", xmpp_stanza_get_name(xmpp_stanza_get_children(item)));
|
||||
|
||||
ret = xmpp_stanza_get_attributes(error, attr, attrlen);
|
||||
/* attr contains both attribute name and value. */
|
||||
assert(ret == 8);
|
||||
|
||||
item = xmpp_stanza_get_child_by_name(error, "error");
|
||||
assert(item != NULL);
|
||||
|
||||
ret = xmpp_stanza_to_text(item, &buf, &buflen);
|
||||
assert(ret == XMPP_EOK);
|
||||
COMPARE(str_error, buf);
|
||||
|
||||
xmpp_free(ctx, buf);
|
||||
xmpp_stanza_release(mood);
|
||||
xmpp_stanza_release(stanza);
|
||||
xmpp_stanza_release(error);
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -122,6 +253,7 @@ int main()
|
||||
|
||||
test_stanza_add_child(ctx);
|
||||
test_stanza_from_string(ctx);
|
||||
test_stanza_error(ctx);
|
||||
|
||||
xmpp_ctx_free(ctx);
|
||||
xmpp_shutdown();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user